software-design/composition-vs-inheritance-why-a-toy-duck-shouldn-t-learn-to-fly.md
Composition vs inheritance: why a toy duck shouldn't learn to fly
A rubber duck that learns to fly is a classic sign that inheritance, the "is a" way of sharing code, has been stretched too far. Every behaviour you add can double the number of classes you need, and Python's multiple inheritance can silently hide one behaviour behind another.
Build a creature from abilities in the page, watch a grid of required classes double as you add one more ability, and compare it with *composition, where a creature simply has* small parts. Includes three Python examples you can run, and a plain rule for when inheritance is still the right tool.

A duck, a toy duck, and a bug
Imagine you are building a small game full of creatures. You write a Duck that can swim and fly. Later you add a RubberDuck, because a rubber duck is a kind of duck, right? Then someone reports that the rubber duck takes off. This article is about why that happens, and about a different way of sharing code that avoids it.
Two terms first. A class is a blueprint for making objects: it lists what an object knows and what it can do. Inheritance lets one class (the subclass) automatically receive everything from another (the parent class), so you do not copy and paste. It is the first tool most people learn for sharing code, and it is very good at one thing and surprisingly bad at another.
Where inheritance bites
Here is the duck family in Python. RubberDuck has an empty body (pass), so it receives everything Duck has:
class Duck:
def swim(self):
return "paddles"
def fly(self):
return "flaps into the air"
class RubberDuck(Duck):
pass
toy = RubberDuck()
print(toy.swim())
print(toy.fly())
Running it prints:
paddles
flaps into the air
The toy flies, because inheritance hands over everything, whether you wanted it or not. You can patch it by overriding fly in RubberDuck, but now the subclass spends its life cancelling things its parent promised. Each new creature adds more cancelling, and the parent class becomes a promise nobody keeps.
The combination problem
The bigger trouble shows up when behaviours mix. Say your monsters can fly, swim, shoot fireballs or burrow, in any combination. Using inheritance you would create a class for each combination you need: FlySwimMonster, FlyShootMonster, FlySwimShootMonster, and so on. With 2 abilities there are 4 combinations (including "none"), with 4 there are 16, with 6 there are 64. Each ability you add doubles the number of possible classes.
Python lets a class inherit from two parents (called multiple inheritance), which looks like an escape. It comes with its own surprise:
class Monster:
def act(self):
return "stands still"
class FlyingMonster(Monster):
def act(self):
return "flies"
class SwimmingMonster(Monster):
def act(self):
return "swims"
class FlySwimMonster(FlyingMonster, SwimmingMonster):
pass
print(FlySwimMonster().act())
print([c.__name__ for c in FlySwimMonster.__mro__])
flies
['FlySwimMonster', 'FlyingMonster', 'SwimmingMonster', 'Monster', 'object']
The monster only flies. Both parents define act, so Python picks one by walking a fixed search order, the method resolution order (MRO), printed on the second line. FlyingMonster comes first and wins; SwimmingMonster.act is never reached. The swimming did not combine, it was hidden.
Composition: build from parts
Composition flips the question. Instead of asking "what is this thing?" it asks "what does this thing have?". A creature is one ordinary class that holds a list of small, separate ability objects and asks each of them to act:
class Fly:
def act(self):
return "flies"
class Swim:
def act(self):
return "swims"
class Creature:
def __init__(self, name, abilities):
self.name = name
self.abilities = abilities
def act(self):
return [f"{self.name} {a.act()}" for a in self.abilities]
imp = Creature("Imp", [Fly(), Swim()])
for line in imp.act():
print(line)
rock = Creature("Rock", [])
print(rock.act())
Imp flies
Imp swims
[]
The imp flies and swims, nothing is hidden, and the rock, which has no abilities, simply does nothing. You never wrote an ImpClass. To get a new kind of creature you list different parts. To add a new ability you write one small class, and every creature can use it straight away.
Try it: count the classes
So is inheritance bad?
No. Inheritance is a good fit when the "is a" statement is true in every way that matters, and the family stays small and shallow. A ValueError really is an Exception, and Python's own error classes are built that way. Trouble starts when you inherit only to reuse code rather than because the relationship is true, or when the tree keeps growing sideways with each new feature.
A well-known piece of advice, "favour object composition over class inheritance", comes from the 1994 book Design Patterns by Gamma, Helm, Johnson and Vlissides. Read it as a lean, not a law: reach for composition first, and use inheritance when it is honestly a true "is a".
What to do in your own code
When you are about to write class B(A), ask two quick questions. Would every method of A make sense on a B, with no cancelling? And can you picture a second, independent way B might need to vary, such as the swimming and the flying above? If the answer to the first is no, or to the second is yes, store a part instead: give your class a field that holds a small object and ask that object to do the work. Parts are easier to test on their own, easy to swap, and they never double the size of your class list.