2010-06-10 13 views
40

Estoy tratando de serializar a largo plazo un conjunto de objetos relacionados por una sólida jerarquía de clases en java, y me gustaría utilizar los búferes de protocolo para hacerlo debido a su simplicidad, rendimiento y facilidad de actualización. Sin embargo, no proporcionan mucho apoyo para el polimorfismo. En este momento, la forma en que lo estoy manejando es teniendo una solución de "un solo mensaje para gobernarlos todos" que tiene un campo requerido de cadena uri que me permite crear una instancia del tipo correcto a través de la reflexión, luego un conjunto de campos opcionales para todo el otras clases posibles que podría serializar, solo una de las cuales se usará (según el valor del campo uri). ¿Hay una mejor manera de manejar el polimorfismo, o esto es tan bueno como lo que obtendré?¿cuál es la forma correcta de hacer polimorfismo con los búferes de protocolo?

+5

Cuidado: en ** proto3 ** las extensiones se han eliminado. – RickyA

Respuesta

0

¿Ha considerado usar extensions? Puede hacer que su campo uri determine el tipo que se va a usar y luego solo cargue las extensiones apropiadas. Si sabe que sus campos son mutuamente excluyentes, entonces puede reutilizar la identificación de campo entre extensiones separadas.

Tiene que manejarlo todo usted mismo porque los búferes de protocolo no están diseñados para ser autodescriptivos más allá de una simple lista de valores. Esto se aborda en la página de técnicas de google.

28

Existen algunas técnicas para implementar el polimorfismo. Trato de cubrir todas aquí: Protocol Buffer Polymorphism

Mi enfoque preferido utiliza anidado extensions: solución

message Animal 
{ 
    extensions 100 to max; 

    enum Type 
    { 
     Cat = 1; 
     Dog = 2; 
    } 

    required Type type = 1; 
} 

message Cat 
{ 
    extend Animal 
    { 
     required Cat animal = 100; // Unique Animal extension number 
    } 

    // These fields can use the full number range. 
    optional bool declawed = 1; 
} 

message Dog 
{ 
    extend Animal 
    { 
     required Dog animal = 101; // Unique Animal extension number 
    } 

    // These fields can use the full number range. 
    optional uint32 bones_buried = 1; 
} 
+6

Me parece (de los experimentos de Python) que este enfoque todavía permite que un mensaje use ambas extensiones al mismo tiempo. Así que no veo cómo esto difiere del enfoque con los campos de mensaje opcionales y el campo de sugerencia de tipo, a excepción de la sintaxis más compleja. – MvG

+1

Además, ¿por qué necesita tipos aquí? ¿No es suficiente usar 'HasExtension' para verificar qué tipo es el mensaje? – Ixanezis

+0

Sí, solo puede usar HasExtension para averiguar si un mensaje de Animal dado contiene una extensión Cat o Dog. –

6

de Jon es correcta y trabajando pero bastante raro (para mí). Pero Protocol Buffers es bastante simple, lo que puede hacer algo por el estilo:

enum Type { 
    FOO = 0; 
    BAR = 1; 
    } 

message Foo { 
    required Type type = 1; 
} 

message Bar { 
    required Type type = 1; 
    required string text = 2; 
} 

Básicamente mensaje de la barra se extiende mensaje de Foo (de lado práctico, por supuesto). La implementación en Java también es simple:

Bar bar = Bar.newBuilder().setType(Type.BAR).setText("example").build(); 
byte[] data = bar.toByteArray(); 

---- 

Foo foo = Foo.parseFrom(data); 
if(foo.getType() == Type.BAR){ 
    Bar bar = Bar.parseFrom(data); 
    System.out.println(bar.getText()); 
} 

Lo sé, no es una solución elegante, pero es simple y lógica.

+0

Esto está bien, si Foo y Bar pertenecen al mismo servicio. El motivo para usar las extensiones de mensaje es permitir que los clientes extiendan mensajes pertenecientes a un servicio. Por lo tanto, un servicio de almacenamiento podría exportar Foo y permitir extensiones, y luego un cliente podría decidir que también desea almacenar datos de Bar dentro de ese Foo. El servicio de almacenamiento publicaría foo.proto como parte de su definición de API, y el cliente extendería eso con bar.proto que importa Foo desde foo.proto. –

12

Esto no responde a la pregunta original, pero puede ser útil para otros que utilicen v3 de Protocol Buffers. La versión 3 no permite la palabra clave extensions. Al ejecutar protoc en el siguiente archivo, se genera un error con el mensaje Extension ranges are not allowed in proto3.

syntax = "proto3"; 

message BaseMessage { 
    extensions 100 to max; 
} 
0

Una solución un poco mejor, para mí, que la respuesta de @ Łukasz Marciniak.

Si Bar extiende Foo, simplemente escriba:

message Bar { 
    optional Foo foo = 1; 
    optional double aDouble = 2; 
} 
message Foo { 
    optional string aString = 1; 
} 

Así que si evoluciona Foo Foo único mensaje es modificado.

23

En proto3 la palabra clave extend ha sido reemplazada. Desde el docs: If you are already familiar with proto2 syntax, the Any type replaces extensions.

syntax = "proto3"; 

import "google/protobuf/any.proto"; 

message Foo { 
    google.protobuf.Any bar = 1; 
} 

Pero cuidado: Any es esencialmente una mancha bytes.La mayoría de las veces es mejor usar Oneof:

syntax = "proto3"; 

message A { 
    string a = 1; 
} 

message B { 
    string b = 1; 
} 

message Foo { 
    oneof bar { 
    A a = 1; 
    B b = 2; 
    } 
} 
Cuestiones relacionadas