Подтвердить что ты не робот

Какой правильный способ сделать полиморфизм с буферами протоколов?

Я пытаюсь долго сериализовать кучу объектов, связанных сильной иерархией классов в java, и я хотел бы использовать буферы протокола для этого из-за их простоты, производительности и простоты обновления. Однако они не обеспечивают много поддержки полиморфизма. Прямо сейчас, как я его обрабатываю, это иметь решение "одно сообщение для всех", у которого есть обязательное строковое поле uri, которое позволяет мне создать экземпляр правильного типа с помощью отражения, затем кучу необязательных полей для всех другие возможные классы, которые я мог бы сериализовать, только один из них будет использоваться (в зависимости от значения поля uri). Есть ли лучший способ справиться с полиморфизмом, или это так хорошо, как я собираюсь получить?

4b9b3361

Ответ 1

Существует несколько методов для реализации полиморфизма. Я стараюсь их охватить здесь: Полиморфизм буфера протоколов

В моем предпочтительном подходе используется вложенный extensions:

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;
}

Ответ 2

В proto3 заменено ключевое слово extend. Из 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;
}

Но будьте осторожны: Any по существу является байтом байта. В большинстве случаев лучше использовать Oneof:

syntax = "proto3";

message A {
    string a = 1;
}

message B {
    string b = 1;
}

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

Ответ 3

Это не является ответом на исходный вопрос, но может быть полезным для других, использующих v3 Protocol Buffers. Версия 3 запрещает ключевое слово extensions. Запуск protoc в следующем файле генерирует ошибку с сообщением Extension ranges are not allowed in proto3.

syntax = "proto3";

message BaseMessage {
  extensions 100 to max;
}

Ответ 4

Решение Jon правильное и работает, но довольно странно (для меня). Но протокольные буферы довольно просты, поэтому вы можете сделать что-то вроде этого:

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

message Foo {
  required Type type = 1;
}

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

В основном панель сообщений расширяет сообщение Foo (с практической стороны, конечно). Реализация на Java тоже проста:

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());
}

Я знал, что это не изящное решение, но оно простое и логичное.

Ответ 6

Считаете ли вы использование extensions? Вы можете указать поле uri для используемого типа, а затем просто загрузить соответствующие расширения. Если вы знаете, что ваши поля являются взаимоисключающими, вы можете повторно использовать идентификатор поля между отдельными расширениями.

Вы должны справиться с этим сами, потому что буферы протокола не предназначены для самостоятельного описания за пределами простого списка значений. Это затронуто на странице методов Google.

Ответ 7

Решение для меня немного лучше, чем ответить @Łukasz Marciniak.

Если Bar расширяет Foo, просто напишите:

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

Итак, если Foo эволюционирует, изменяется только сообщение Foo.