Inheritance vs. Aggregation

106,844

Solution 1

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.

Solution 2

At the beginning of GOF they state

Favor object composition over class inheritance.

This is further discussed here

Solution 3

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.

Solution 4

Here's my most common argument:

In any object-oriented system, there are two parts to any class:

  1. Its interface: the "public face" of the object. This is the set of capabilities it announces to the rest of the world. In a lot of languages, the set is well defined into a "class". Usually these are the method signatures of the object, though it varies a bit by language.

  2. Its implementation: the "behind the scenes" work that the object does to satisfy its interface and provide functionality. This is typically the code and member data of the object.

One of the fundamental principles of OOP is that the implementation is encapsulated (ie:hidden) within the class; the only thing that outsiders should see is the interface.

When a subclass inherits from a subclass, it typically inherits both the implementation and the interface. This, in turn, means that you're forced to accept both as constraints on your class.

With aggregation, you get to choose either implementation or interface, or both -- but you're not forced into either. The functionality of an object is left up to the object itself. It can defer to other objects as it likes, but it's ultimately responsible for itself. In my experience, this leads to a more flexible system: one that's easier to modify.

So, whenever I'm developing object-oriented software, I almost always prefer aggregation over inheritance.

Solution 5

I gave an answer to "Is a" vs "Has a" : which one is better?.

Basically I agree with other folks: use inheritance only if your derived class truly is the type you're extending, not merely because it contains the same data. Remember that inheritance means the subclass gains the methods as well as the data.

Does it make sense for your derived class to have all the methods of the superclass? Or do you just quietly promise yourself that those methods should be ignored in the derived class? Or do you find yourself overriding methods from the superclass, making them no-ops so no one calls them inadvertently? Or giving hints to your API doc generation tool to omit the method from the doc?

Those are strong clues that aggregation is the better choice in that case.

Share:
106,844

Related videos on Youtube

gigimon
Author by

gigimon

Software Developer and Technology Consultant

Updated on September 14, 2020

Comments

  • gigimon
    gigimon almost 4 years

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

    1. 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.

    2. 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.

  • Bill Karwin
    Bill Karwin over 15 years
    Evolving requirements means your OO design will inevitably be wrong. Inheritance vs. aggregation is just the tip of that iceberg. You can't architect for all possible futures, so just go with the XP idea: solve today's requirements and accept that you may have to refactor.
  • remkohdev
    remkohdev over 15 years
    I like this line of enquiry and questioning. Concerned about blurrung is-a, has-a, and uses-a though. I start with interfaces (along with identifying the implemented abstractions I want interfaces to). The additional level of indirection is invaluable. Don't follow your aggregation conclusion.
  • gigimon
    gigimon over 15 years
    That's pretty much my point though. Both inheritance and aggregation are methods to solve the problem. One of those methods incurs all sorts of penalties when you have to solve problems tomorrow.
  • gigimon
    gigimon over 15 years
    In my mind, aggregation+interfaces are the alternative to inheritance/subclassing; you can't do polymorphism based on aggregation alone.
  • gigimon
    gigimon over 15 years
    The "reuse almost all functionality of a class" is the one time I do actually prefer inheritance. What I'd really like is a language that has the ability to easily say "delegate to this aggregated object for these specific methods"; that's the best of both worlds.
  • Toon Krijthe
    Toon Krijthe over 15 years
    I'm not sure about other languages, but Delphi has a mechanism, that let members implement part of the interface.
  • remkohdev
    remkohdev over 15 years
    I would think that you use an abstract class to define an interface and then use direct inheritance for each concrete implementation class (making it pretty shallow). Any aggregation is under the concrete implementation.
  • remkohdev
    remkohdev over 15 years
    If you need additional interfaces than that, return them via methods on the interface of the main-level abstracted interface, rinse repeat.
  • LCJ
    LCJ almost 12 years
    Do you think, aggregation is better in this scenario? A book is a SellingItem, A DigitalDisc is a SelliingItem codereview.stackexchange.com/questions/14077/…
  • Val
    Val over 11 years
    Also, difference is summed up nicely in the very question that you are supposed to respond to. I would like that people first read questions before start translating. Do yo blindly promote design patters to become a member of the elite club?
  • cantfindaname88
    cantfindaname88 over 11 years
    Objective C uses Protocols that let you decide whether your function is required or optional.
  • Alireza Rahmani khalili
    Alireza Rahmani khalili about 6 years
    I do not like this approach, it's more about composition
  • blongho
    blongho almost 5 years
    Great answer with a practical example. I would design the Pet class with a method called makeSound and let each subclass implement their own kind of sound. This will then help in situations where you have many pets and just do for_each pet in the list of pets, pet.makeSound.
  • KevDog
    KevDog about 2 years
    What if my pet is a goldfish?