如何使用proto-buffers有效地完成继承

时间:2014-01-31 06:44:30

标签: inheritance protocol-buffers

我有一个基类消息

message Animal {
     optional string name = 1;
     optional int32 age = 2;  
}

和扩展动物的子类

message Dog{
     optional string breed = 1;
}

因此,在构建狗消息时,我应该能够设置Animal的所有字段。我知道关于这样做的方法(在狗的消息中再次声明所有的动物场)但是可以简单有效地使用protobuffers吗? 我也学习了扩展,我明白它只是用来为已经存在的消息添加一个新字段,所以它不应该被误解为实现继承的可能解决方案。

是否可以使用protobuffers的扩展来实现上述简单设计?

1 个答案:

答案 0 :(得分:2)

有几种不同的方法可以实现"继承"在Protocol Buffers中。你想要哪一个取决于你的用例。

选项1:子类包含超类

message Animal {
  optional string name = 1;
  optional int32 age = 2;  
}

message Dog {
  required Animal animal = 1;
  optional string breed = 2;
}

此处Dog包含Animal,因此包含Animal的所有信息。

如果您不需要支持向下转换,则此方法有效。也就是说,你永远不必说"这是Animal一个Dog?"因此,任何可能需要访问Dog字段的内容都需要Dog作为输入,而不是Animal。对于许多用例,这很好。

选项2:超类包含所有子类

message Animal {
  optional string name = 1;
  optional int32 age = 2;

  // Exactly one of these should be filled in, depending on the species.
  optional Dog dog = 100;
  optional Cat cat = 101;
  optional Axolotl axolotl = 102;
  // ...
}

在这种方法中,给定Animal,您可以确定它是哪种动物并访问物种特定信息。也就是说,你可以向下投。

如果你有一个"子类"的固定列表,这很有效。只需列出所有这些,并记录只应填写其中一个字段。如果有很多子类,您可能需要添加一个枚举字段来指示哪一个存在,这样您就不会必须单独检查has_dog()has_cat()has_mouse(),...

选项3:扩展

message Animal {
  optional string name = 1;
  optional int32 age = 2;
  extensions 100 to max;  // Should contain exactly one "species" extension.
}

message Dog {
  optional string breed = 1;
}

extend Animal {
  optional Dog animal_dog = 100;
  // (The number must be unique among all Animal extensions.)
}

此选项实际上与选项#2在语义上相同!唯一的区别是,不是在Animal中声明大量可选字段,而是将它们声明为扩展名。每个扩展程序都有效地向Animal添加了一个新字段,但您可以在其他文件中声明它们,因此您不必拥有一个中央列表,而其他人可以在不编辑代码的情况下添加新扩展名。由于每个扩展的行为就像常规字段一样,除了用于声明和访问它的语法有点奇怪之外,所有内容都与选项#2的行为相同。 (事实上​​,在这里的示例中,线编码甚至是相同的,因为我们使用100作为分机号码,而在选项2中我们使用100作为场号。)

这是理解扩展的技巧。许多人感到困惑,因为他们试图等同于"延伸"以面向对象的语言继承。别这么做!请记住,扩展的行为就像字段一样,这里的选项2和3实际上是相同的。它不是继承......但它可以解决同样的问题。