Composition Over Inheritance

Composition Over Inheritance

Inheritance in Python is nice since it facilitates a great deal of code reuse. We put common data and behavior into a Base class and then implement any extra data and behavior needed in derived classes. Therefore when using inheritance, you are making use of an “is a” relationship. For example, a Car is a Vehicle. Another approach to code reuse when using the object-oriented style of programming in Python is Composition. Composition has a different approach wherein a “has a” relationship is used. Basically in composition, you assemble complex objects out of more simple ones. You build objects out of other objects. For example, you could build a Car object that has a Engine object, or has a Tire object, and so on. We’ll take a look at using composition over inheritance in this lesson.

Inheritance Vs Composition

To the left of the yellow line is an example of what Inheritance might look like conceptually. On the right is an example of composition.

Inheritance vs Composition

Composition In Code

In the code below, we make use of composition. You’ll notice that there is no inheritance in any of the classes defined. What we do see however is the approach of instantiating an Engine object, and then assigning that object to a property of a Tractor object. So you see, this is an example of building up a more complex class out of a more simple class. This is composition in action.

# Using composition to build complex objects
class Tractor():
    def __init__(self, model, make, engine=None):
        self.model = model
        self.make = make

        # Use references to other objects, like Engine and Implement
        self.engine = engine
        self.implements = []

    def addimplement(self, implement):

    def get_tractor_implements(self):
        return self.implements

class Engine():
    def __init__(self, cylinders, horsepower):
        self.cylinders = cylinders
        self.horsepower = horsepower

    def __str__(self):
        return f"{self.cylinders} cylinder {self.horsepower} horsepower"

class Implement():
    def __init__(self, attachment_type):
        self.attachment_type = attachment_type

engine1 = Engine(3, 25)
tractor1 = Tractor("John Deere", "1025R", engine1)

tractor1.addimplement(Implement("Mowing Deck"))

print(f"This is a {tractor1.model} tractor.")
print(f"It has {tractor1.engine} engine.")
attachments = tractor1.get_tractor_implements()
print("The attachments it has include: ")
for attachment in attachments:
    print(" - " + attachment.attachment_type)
This is a John Deere tractor.
It has 3 cylinder 25 horsepower engine.
The attachments it has include:
 - Loader
 - Backhoe
 - Mowing Deck
 - Snowblower