Composition is a design principle where one class contains objects of other classes to reuse functionality instead of inheriting it.

Inheritance vs Composition

FeatureInheritanceComposition
GoalReuse behavior via “is-a” relationshipReuse behavior via “has-a” relationship
CouplingTight couplingLoose coupling
FlexibilityLess flexible to changeMore flexible and modular
Code ReuseInherits all parent behaviorReuses specific behavior only when needed
Use WhenNatural hierarchy existsYou want to compose behavior dynamically
DownsidesFragile base class problem, tight couplingMay involve more boilerplate

When to use Composition?

  • You want to combine behaviors flexibly.

  • You’re designing for loose coupling and testability.

  • You want to avoid unintended inheritance side effects.

Inheritance vs Composition Minimal example

Inheritance
class Animal:
    def speak(self):
        print("Animal sound")

class Dog(Animal):
    def speak(self):
        print("Woof!")

dog = Dog()
dog.speak()  # Output: Woof!
Composition
class Engine:
    def start(self):
        print("Engine started")

class Car:
    def __init__(self):
        self.engine = Engine()

    def start(self):
        self.engine.start()

car = Car()
car.start()  # Output: Engine started