프로토 타입 디자인 패턴


19

Java 기반 서비스에 대한 Google 프로토콜 버퍼를 평가하고 있지만 언어와 무관 한 패턴을 기대하고 있습니다. 두 가지 질문이 있습니다.

첫 번째는 광범위한 일반적인 질문입니다.

사람들이 사용하는 패턴은 무엇입니까? 상기 패턴은 클래스 구성 (예를 들어,. 프로토 파일 당 메시지, 패키징 및 분배) 및 메시지 정의 (예를 들어, 반복 된 필드 대 반복 된 캡슐화 된 필드 *) 등과 관련된다.

Google Protobuf 도움말 페이지와 공개 블로그에는 이런 종류의 정보가 거의 없지만 XML과 같은 기존 프로토콜에 대한 수많은 정보가 있습니다.

또한 다음 두 가지 패턴에 대한 구체적인 질문이 있습니다.

  1. .proto 파일로 메시지를 표현하고 별도의 jar 파일로 패키지하여 서비스 소비자를 대상으로 전달하십시오. 기본적으로 내가 생각하는 기본 접근 방식입니다.

  2. 적어도 두 가지 방법을 지원하는 계약을 구현하는 각 메시지 주위에 수작업으로 포장 된 래퍼 (하위 클래스가 아님)를 포함하십시오 (T는 래퍼 클래스, V는 메시지 클래스 (제네릭을 사용하지만 간략 함을 위해 간단한 구문 사용)). :

    public V toProtobufMessage() {
        V.Builder builder = V.newBuilder();
        for (Item item : getItemList()) {
            builder.addItem(item);
        }
        return builder.setAmountPayable(getAmountPayable()).
                       setShippingAddress(getShippingAddress()).
                       build();
    }
    
    public static T fromProtobufMessage(V message_) { 
        return new T(message_.getShippingAddress(), 
                     message_.getItemList(),
                     message_.getAmountPayable());
    }
    

내가 의해 도입 된 복잡성을 멀리 숨길 수 I (2)를 참조 장점 중 하나는 V.newBuilder().addField().build()과 같은 몇 가지 의미있는 방법을 추가 isOpenForTrade()또는 isAddressInFreeDeliveryZone()등 내 래퍼에. (2)에서 볼 수있는 두 번째 장점은 클라이언트가 변경 불가능한 객체 (래퍼 클래스에서 강제 할 수있는 것)를 처리한다는 것입니다.

(2)의 단점 중 하나는 코드를 복제하고 래퍼 클래스를 .proto 파일과 동기화해야한다는 것입니다.

누구든지 두 가지 접근법 중 하나에 대해 더 나은 기술이나 비평을 가지고 있습니까?


* 반복 된 필드를 캡슐화함으로써 다음과 같은 메시지를 의미합니다.

message ItemList {
    repeated item = 1;
}

message CustomerInvoice {
    required ShippingAddress address = 1;
    required ItemList = 2;
    required double amountPayable = 3;
}

이와 같은 메시지 대신 :

message CustomerInvoice {
    required ShippingAddress address = 1;
    repeated Item item = 2;
    required double amountPayable = 3;
}

나는 후자를 좋아하지만 그것에 대한 논쟁을 기뻐합니다.


새 태그를 만들려면 12 포인트가 더 필요하며 protobuf가이 게시물의 태그 여야한다고 생각합니다.
Apoorv Khurasia

답변:


13

내가 일하는 곳에서 프로토 타입 사용을 감추기로 결정했습니다. 우리는 .proto응용 프로그램간에 파일을 배포하지 않지만 protobuf 인터페이스를 노출하는 응용 프로그램은 대화 할 수있는 클라이언트 라이브러리를 내 보냅니다.

이 프로토 타입 노출 응용 프로그램 중 하나에서만 작업했지만 각 프로토 타입 메시지는 도메인의 일부 개념에 해당합니다. 각 개념마다 일반적인 Java 인터페이스가 있습니다. 그런 다음 구현의 인스턴스를 가져와 적절한 메시지 객체를 생성하고 메시지 객체를 가져와 인터페이스 구현의 인스턴스를 생성 할 수있는 변환기 클래스가 있습니다 (일반적으로 단순한 익명 또는 로컬 클래스가 정의 됨) 변환기 내부). 프로토 타입 생성 메시지 클래스와 변환기는 응용 프로그램과 클라이언트 라이브러리 모두에서 사용되는 라이브러리를 형성합니다. 클라이언트 라이브러리는 연결 설정 및 메시지 전송 및 수신을위한 소량의 코드를 추가합니다.

그런 다음 클라이언트 응용 프로그램은 클라이언트 라이브러리를 가져 와서 보내려는 인터페이스의 구현을 제공합니다. 실제로, 양측은 후자를한다.

분명히 말하면, 클라이언트가 파티 초대장을 보내는 요청-응답주기가 있고 서버가 RSVP로 응답하는 경우 관련 사항은 다음과 같습니다.

  • .proto파일에 작성된 PartyInvitation 메시지
  • PartyInvitationMessage 에 의해 생성 된 클래스 protoc
  • PartyInvitation 공유 라이브러리에 정의 된 인터페이스
  • ActualPartyInvitation, PartyInvitation클라이언트 앱에 의해 정의 된 구체적인 구현 (실제로는 그렇게 부르지 않습니다!)
  • StubPartyInvitationPartyInvitation공유 라이브러리에 의해 정의 된 간단한 구현
  • PartyInvitationConvertera PartyInvitation를 a로 PartyInvitationMessage, a PartyInvitationMessage를 a로StubPartyInvitation
  • .proto파일에 작성된 RSVP 메시지
  • RSVPMessage 에 의해 생성 된 클래스 protoc
  • RSVP 공유 라이브러리에 정의 된 인터페이스
  • ActualRSVP, RSVP서버 앱에 의해 정의 된 구체적인 구현 (실제로는 그렇게하지는 않습니다!)
  • StubRSVPRSVP공유 라이브러리에 의해 정의 된 간단한 구현
  • RSVPConverter, 변환 할 수있는 RSVPRSVPMessage, 그리고이 RSVPMessageA를StubRSVP

실제 구현과 스텁 구현이 분리 된 이유는 실제 구현이 일반적으로 JPA 맵핑 엔티티 클래스이기 때문입니다. 서버는 이들을 생성 및 유지하거나 데이터베이스에서 쿼리 한 다음 프로토 타입 계층으로 전달하여 전송합니다. 연결의 수신 측에서 해당 클래스의 인스턴스를 작성하는 것이 적절하다고 생각되지 않았습니다. 지속성 컨텍스트에 묶여 있지 않기 때문입니다. 또한 엔티티는 종종 유선을 통해 전송되는 것보다 더 많은 데이터를 포함하므로 수신 측에서 손상되지 않은 객체를 생성하는 것조차 불가능합니다. 이것이 우리가 메시지보다 하나 더 많은 수업을 남겼 기 때문에 이것이 올바른 조치라고 확신하지는 않습니다.

실제로, 나는 protobuf를 사용하는 것이 좋은 생각이라고 전적으로 확신하지는 않는다. 우리가 평범한 오래된 RMI와 직렬화를 고수했다면 거의 많은 객체를 만들 필요가 없었을 것입니다. 대부분의 경우 엔티티 클래스를 직렬화 가능으로 표시하고 계속 사용할 수 있습니다.

이제 모든 것을 말했듯이 모듈 간 통신에 프로토 타입을 많이 사용하는 코드베이스에서 Google에서 일하는 친구가 있습니다. 그들은 완전히 다른 접근법을 취합니다. 생성 된 메시지 클래스를 전혀 랩핑하지 않고 코드에 깊이 전달합니다. 인터페이스를 유연하게 유지하는 간단한 방법이기 때문에 이것은 좋은 것으로 보입니다. 메시지가 진화 할 때 동기화 할 스캐 폴딩 코드는 없으며 생성 된 클래스는 hasFoo()시간이 지남에 따라 추가 된 필드의 존재 여부를 감지하기 위해 코드를받는 데 필요한 모든 방법을 제공합니다 . 그러나 Google에서 일하는 사람들은 (a) 다소 영리하고 (b) 약간 견딜 수있는 경향이 있습니다.


어느 시점에서, 나는 표준 직렬화를위한 대체 대체품으로 JBoss Serialization 을 사용하는 방법을 조사했습니다 . 꽤 빨랐습니다. 그러나 프로토 버프만큼 빠르지는 않습니다.
Tom Anderson

jackson2를 사용한 JSON 직렬화도 매우 빠릅니다. GBP에 대해 싫어하는 것은 메인 인터페이스 클래스의 불필요한 복제입니다.
Apoorv Khurasia

0

앤더슨의 답변에 추가하기 위해 메시지를 영리하게 다른 메시지에 중첩시키고 그것을 과도하게 처리하는 데 미세한 선이 있습니다. 문제는 각 메시지가 배후에 새로운 클래스를 만들고 데이터에 대한 모든 종류의 접근 자와 처리기를 만드는 것입니다. 그러나 데이터를 복사하거나 하나의 값을 변경하거나 메시지를 비교해야 할 경우 비용이 발생합니다. 데이터가 많거나 시간이 지나면 이러한 프로세스가 매우 느리고 고통 스러울 수 있습니다.


2
이것은 접선 주석처럼 읽습니다. 답변하는 방법
gnat

1
글쎄요, 그렇지 않습니다 : 도메인이 없습니다. 결국에는 모든 문구 문제가 있습니다. (오, 저는 C ++로 모든 것을 개발하고 있지만 문제는 없어야합니다)
Marko Bencik
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.