How to accomplish inheritance effectively using proto-buffers

前端 未结 1 1023
执笔经年
执笔经年 2021-01-22 22:33

I have a base class message

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

and the sub-class which extends an

相关标签:
1条回答
  • 2021-01-22 23:15

    There are a few different ways to accomplish "inheritance" in Protocol Buffers. Which one you want depends on your use case.

    Option 1: Subclass contains superclass

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

    Here, Dog contains an Animal, thus contains all the information of Animal.

    This approach works if you do not need to support down-casting. That is, you never have to say "Is this Animal a Dog?" So, anything which might need to access the fields of Dog needs to take a Dog as its input, not an Animal. For many use cases, this is fine.

    Option 2: Superclass contains all subclasses

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

    In this approach, given an Animal, you can figure out which animal it is and access the species-specific information. That is, you can down-cast.

    This works well if you have a fixed list of "subclasses". Just list all of them, and document that only one of the fields should be filled in. If there are a lot of subclasses, you might want to add an enum field to indicate which one is present, so that you don't have to individually check has_dog(), has_cat(), has_mouse(), ...

    Option 3: Extensions

    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.)
    }
    

    This option is actually semantically identical to option #2! The only difference is that instead of declaring lots of optional fields inside Animal, you are declaring them as extensions. Each extension effectively adds a new field to Animal, but you can declare them in other files, so you don't have to have one central list, and other people can add new extensions without editing your code. Since each extension behaves just like a regular field, other than having somewhat-weird syntax for declaring and accessing it, everything behaves the same as with option #2. (In fact, in the example here, the wire encoding would even be the same, since we used 100 as the extension number, and in option 2 we used 100 as the field number.)

    This is the trick to understanding extensions. Lots of people get confused because they try to equate "extend" to inheritance in object-oriented languages. Don't do that! Just remember that extensions behave just like fields, and that options 2 and 3 here are effectively the same. It's not inheritance... but it can solve the same problems.

    0 讨论(0)
提交回复
热议问题