Inheritance vs. Aggregation

There are two schools of thought on how to best extend, enhance, and reuse code in an object-oriented system:

  • Inheritance: extend the functionality of a class by creating a subclass. Override superclass members in the subclasses to provide new functionality. Make methods abstract/virtual to force subclasses to "fill-in-the-blanks" when the superclass wants a particular interface but is agnostic about its implementation.

  • Aggregation: create new functionality by taking other classes and combining them into a new class. Attach an common interface to this new class for interoperability with other code.

  • What are the benefits, costs, and consequences of each? Are there other alternatives?

    I see this debate come up on a regular basis, but I don't think it's been asked on Stack Overflow yet (though there is some related discussion). There's also a surprising lack of good Google results for it.


    It's not a matter of which is the best, but of when to use what.

    In the 'normal' cases a simple question is enough to find out if we need inheritance or aggregation.

  • If The new class is more or less as the original class. Use inheritance. The new class is now a subclass of the original class.
  • If the new class must have the original class. Use aggregation. The new class has now the original class as a member.
  • However, there is a big gray area. So we need several other tricks.

  • If we have used inheritance (or we plan to use it) but we only use part of the interface, or we are forced to override a lot of functionality to keep the correlation logical. Then we have a big nasty smell that indicates that we had to use aggregation.
  • If we have used aggregation (or we plan to use it) but we find out we need to copy almost all of the functionality. Then we have a smell that points in the direction of inheritance.
  • To cut it short. We should use aggregation if part of the interface is not used or has to be changed to avoid an illogical situation. We only need to use inheritance, if we need almost all of the functionality without major changes. And when in doubt, use Aggregation.

    An other possibility for, the case that we have an class that needs part of the functionality of the original class, is to split the original class in a root class and a sub class. And let the new class inherit from the root class. But you should take care with this, not to create an illogical separation.

    Lets add an example. We have a class 'Dog' with methods: 'Eat', 'Walk', 'Bark', 'Play'.

    class Dog
      Eat;
      Walk;
      Bark;
      Play;
    end;
    

    We now need a class 'Cat', that needs 'Eat', 'Walk', 'Purr', and 'Play'. So first try to extend it from a Dog.

    class Cat is Dog
      Purr; 
    end;
    

    Looks, alright, but wait. This cat can Bark (Cat lovers will kill me for that). And a barking cat violates the principles of the universe. So we need to override the Bark method so that it does nothing.

    class Cat is Dog
      Purr; 
      Bark = null;
    end;
    

    Ok, this works, but it smells bad. So lets try an aggregation:

    class Cat
      has Dog;
      Eat = Dog.Eat;
      Walk = Dog.Walk;
      Play = Dog.Play;
      Purr;
    end;
    

    Ok, this is nice. This cat does not bark anymore, not even silent. But still it has an internal dog that wants out. So lets try solution number three:

    class Pet
      Eat;
      Walk;
      Play;
    end;
    
    class Dog is Pet
      Bark;
    end;
    
    class Cat is Pet
      Purr;
    end;
    

    This is much cleaner. No internal dogs. And cats and dogs are at the same level. We can even introduce other pets to extend the model. Unless it is a fish, or something that does not walk. In that case we again need to refactor. But that is something for an other time.


    At the beginning of GOF they state

    Favor object composition over class inheritance.

    This is further discussed here


    The difference is typically expressed as the difference between "is a" and "has a". Inheritance, the "is a" relationship, is summed up nicely in the Liskov Substitution Principle. Aggregation, the "has a" relationship, is just that - it shows that the aggregating object has one of the aggregated objects.

    Further distinctions exist as well - private inheritance in C++ indicates a "is implemented in terms of" relationship, which can also be modeled by the aggregation of (non-exposed) member objects as well.

    链接地址: http://www.djcxy.com/p/53942.html

    上一篇: 吸气剂和定型剂设计不佳吗? 看到矛盾的建议

    下一篇: 继承与聚合