协议缓冲区中的多态性3

Ada*_*tan 7 polymorphism protocol-buffers microservices protobuf-3

目前的设计

我正在重构一些退出的API代码,该代码返回用户的事件提要.API是一个普通的RESTful API,当前的实现只是查询数据库并返回一个feed.

代码很长且很麻烦,所以我决定将feed生成移动到将从API服务器调用的微服务.

新设计

为了解耦,我认为数据可以作为Protobuf对象从API服务器来回移动到微服务.这样,我可以改变两端的编程语言,仍然享受protobuf的类型安全和纤薄的尺寸.

在此输入图像描述

问题

Feed包含多种类型(例如,喜欢,图片和语音消息).将来可以添加新类型.例如,它们共享一些属性时间戳和标题 - 但除此之外,它们可能完全不同.

在经典的OOP中,解决方案很简单 - FeedItem所有feed项继承的基类,以及Feed包含一系列FeedItem类的类.

如何在Protocol Buffers 3中表达多态性的概念,或者至少在列表中启用不同类型的消息?

我检查了什么

  • Oneof:"一个不可重复的".
  • Any:太宽泛了(就像Java的List<Object>.

Kir*_*ril 2

序列化协议的答案是使用基于鉴别器的多态性。传统的面向对象继承是一种具有一些非常糟糕的特征的形式。在 OpenAPI 等较新的协议中,概念更加清晰。

让我解释一下它是如何与 proto3 一起工作的

首先,您需要声明您的多态类型。假设我们解决经典的动物物种问题,其中不同物种具有不同的特性。我们首先需要为所有动物定义一个根类型,以识别物种。然后我们声明扩展基本类型的 Cat 和 Dog 消息。请注意,判别器species在所有 3 个中都有投影:

 message BaseAnimal {
   string species = 1;
 }

 message Cat {
   string species = 1;
   string coloring = 10;
 }

 message Dog {
   string species = 1;
   int64 weight = 10;
 }
Run Code Online (Sandbox Code Playgroud)

这是一个简单的 Java 测试,用于演示实际情况如何工作

    ByteArrayOutputStream os = new ByteArrayOutputStream(1024);

    // Create a cat we want to persist or send over the wire
    Cat cat = Cat.newBuilder().setSpecies("CAT").setColoring("spotted")
            .build();

    // Since our transport or database works for animals we need to "cast"
    // or rather convert the cat to BaseAnimal
    cat.writeTo(os);
    byte[] catSerialized = os.toByteArray();
    BaseAnimal forWire = BaseAnimal.parseFrom(catSerialized);
    // Let's assert before we serialize that the species of the cat is
    // preserved
    assertEquals("CAT", forWire.getSpecies());

    // Here is the BaseAnimal serialization code we can share for all
    // animals
    os = new ByteArrayOutputStream(1024);
    forWire.writeTo(os);
    byte[] wireData = os.toByteArray();

    // Here we read back the animal from the wire data
    BaseAnimal fromWire = BaseAnimal.parseFrom(wireData);
    // If the animal is a cat then we need to read it again as a cat and
    // process the cat going forward
    assertEquals("CAT", fromWire.getSpecies());
    Cat deserializedCat = Cat.parseFrom(wireData);

    // Check that our cat has come in tact out of the serialization
    // infrastructure
    assertEquals("CAT", deserializedCat.getSpecies());
    assertEquals("spotted", deserializedCat.getColoring());
Run Code Online (Sandbox Code Playgroud)

整个技巧是 proto3 绑定保留它们不理解的属性并根据需要序列化它们。通过这种方式,我们可以实现 proto3 强制转换(转换),从而在不丢失数据的情况下更改对象的类型。

请注意,“proto3 转换”是非常不安全的操作,只有在对鉴别器进行适当检查后才应应用。在我的例子中,你可以毫无问题地把一只猫扔给一只狗。下面的代码失败

    try {
        Dog d = Dog.parseFrom(wireData);
        fail();
    } catch(Exception e) {
        // All is fine cat cannot be cast to dog
    }
Run Code Online (Sandbox Code Playgroud)

当同一索引处的属性类型匹配时,可能会出现语义错误。在我的示例中,其中索引 10 是狗中的 int64 或猫中的字符串 proto3 将它们视为不同的字段,因为它们在线上的类型代码不同。在某些情况下,类型可能是字符串并且结构 proto3 实际上可能会抛出一些异常或产生完整的垃圾。