Protocol Buffer 中的多态性 3

标签 polymorphism protocol-buffers microservices protobuf-3

目前的设计

我正在重构一些为用户返回事件源的现有 API 代码。 API 是一个普通的 RESTful API,当前的实现只是简单地查询一个 DB 并返回一个提要。

代码又长又麻烦,所以我决定将提要生成转移到将从 API 服务器调用的微服务中。

新设计

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

enter image description here

问题

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

在经典的 OOP 中,解决方案很简单 - 一个基础 FeedItem所有提要项都继承自的类,以及 Feed包含 FeedItem 序列的类类。

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

什么have I checked

  • Oneof :“一个不可重复”。
  • Any : 太宽泛(如 Java 的 List<Object>
  • 最佳答案

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

    让我解释一下它是如何与 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;
     }
    

    这是一个简单的 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());
    

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

    请注意,“proto3 cast”是非常不安全的操作,只能在对鉴别器进行适当检查后应用。在我的示例中,您可以毫无问题地将猫扔给狗。下面的代码失败
        try {
            Dog d = Dog.parseFrom(wireData);
            fail();
        } catch(Exception e) {
            // All is fine cat cannot be cast to dog
        }
    

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

    关于 Protocol Buffer 中的多态性 3,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40575493/

    相关文章:

    java - Protocol Buffer : Enum issue

    android - Google/Firebase android SDK 是否使用 GRPC?

    c# - 微服务之间如何建立通信?

    protocol-buffers - Google Protobuf 服务中的 RPC

    docker - 如何通过Zuul代理从一种微服务访问另一种微服务

    jpa - 使用 micronaut-data-hibernate-jpa 库连接到 jpa 时出现错误

    c# - 在保持多态性的同时更改子类的属性访问器的适当方法是什么?

    c++ - 来自普通函数的虚函数调用

    haskell - 为什么类型 f (f b c) (f (f a b) (f a c)) 与 (.) 不匹配?

    c++ - 我误用了继承权吗?