我有一个基类消息
message Animal {
optional string name = 1;
optional int32 age = 2;
}
和扩展动物的子类
message Dog{
optional string breed = 1;
}
因此,在构建狗消息时,我应该能够设置Animal的所有字段。我知道关于这样做的方法(在狗的消息中再次声明所有的动物场)但是可以简单有效地使用protobuffers吗? 我也学习了扩展,我明白它只是用来为已经存在的消息添加一个新字段,所以它不应该被误解为实现继承的可能解决方案。
是否可以使用protobuffers的扩展来实现上述简单设计?
答案 0 :(得分:2)
有几种不同的方法可以实现"继承"在Protocol Buffers中。你想要哪一个取决于你的用例。
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
。对于许多用例,这很好。
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()
,...
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实际上是相同的。它不是继承......但它可以解决同样的问题。