OOP Concepts – How Do Inheritance and Composition Differ?

compositioninheritanceobject-oriented-design

I'm wondering about the differences between inheritance and composition examined with concrete code relevant arguments.

In particular my example was:

Inheritance:

class Do:
    def do(self):
        self.doA()
        self.doB()

    def doA(self):
        pass

    def doB(self):
        pass

class MyDo(Do):
    def doA(self):
        print("A")

    def doB(self):
        print("B")

x=MyDo()

vs Composition:

class Do:
    def __init__(self, a, b):
        self.a=a
        self.b=b

    def do(self):
        self.a.do()
        self.b.do()

x=Do(DoA(), DoB())

(Note for composition I'm missing code so it's not actually shorter)

Can you name particular advantages of one or the other?

I'm think of:

  • composition is useful if you plan to reuse DoA() in another context
  • inheritance seems easier; no additional references/variables/initialization
  • method doA can access internal variable (be it a good or bad thing 🙂 )
  • inheritance groups logic A and B together; even though you could equally introduce a grouped delegate object
  • inheritance provides a preset class for the users; with composition you'd have to encapsule the initialization in a factory so that the user does have to assemble the logic and the skeleton

Basically I'd like to examine the implications of inheritance vs composition. I heard often composition is preferred, but I'd like to understand that by example.

Of course I can always start with one and refactor later to the other.

Best Answer

Conceptually speaking, composition models "consists of" relationships, whereas inheritance models "is a".

Using the car analogy, "a car has wheels" is a textbook example for composition, and it makes sense to have a class Wheel, and a class Car with a property wheels of type Wheel[].

In theory, an example of inheritance would be "a truck is a vehicle": properties common to all vehicles can be implemented in class Vehicle, while those specific to trucks can be implemented in class Truck.

The truck example, however, also illustrates the problem of the inheritance approach: what if you have to make your vehicle class polymorphic not only for the vehicles purpose (passengers vs. freight), but also for fuel type? You'd have to create four classes to cover passenger cars and freight vehicles, as well as diesel vs. gasoline powered. Add another orthogonal property, and the number of classes doubles again. Worse yet, you have to decide which of these orthogonal properties comes first in the class hierarchy: is it Vehicle -> DieselVehicle -> DieselFreightVehicle -> Truck, or is it Vehicle -> FreightVehicle -> DieselFreightVehicle -> Truck? Either way, you have to duplicate some functionality, either the freight-specific things, or the diesel-specific things. The solution is to use composition anyway: A vehicle has an engine (which can be diesel- or gasonline-powered), and a cargo type (which can be passengers or freight); you don't need a truck class anymore, because your vehicle class can already model all sorts of vehicles by combining suitable engines and cargo types. Note that the components are still polymorphic, but the container object is not. The trick is to keep polymorphism one-dimensional, that is, each polymorphic hierarchy models one of many orthogonal properties, such as engine type, freight type, etc.

This is why people say "favor composition over inheritance"; of course you are still inheriting, but you have separated your inheritance into independent strains.

Related Topic