Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Composition vs Inheritance

Choose whether an object should be a specialized kind of something, or a collaboration of smaller reusable parts.

Once you understand classes, encapsulation, inheritance, and abstraction, the next design decision is architectural: should new behavior come from subclassing, or from plugging objects together?


Why This Matters

In business software, systems change constantly. Discount rules change. Notification channels change. Data sources change. Composition often makes these changes cheaper because you can swap one component without rebuilding the full class hierarchy.

Learning Goals

  • identify when inheritance models a true is-a relationship

  • identify when composition models a better has-a relationship

  • compare flexibility, reuse, and maintenance tradeoffs

  • prepare for the next notebook on interactions between collaborating classes

Decision Rule

Use inheritance when the child is genuinely a specialized version of the parent. Use composition when the object should be assembled from services, strategies, or helper objects.

QuestionInheritanceComposition
Relationshipis-ahas-a / uses-a
Strengthsimple reuse of shared behaviorhigh flexibility and swapping
Riskrigid hierarchies, deep treesmore objects to coordinate
ExampleSavingsAccount is an AccountCheckoutService has a PaymentStrategy

Visual Intuition

Caption: Inheritance extends one parent type. Composition builds behavior by wiring together several collaborators.*

Worked Example: Flexible Checkout with Composition

No subclass explosion is needed here. We combine small parts to create the behavior we want.

class Employee:
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary

    def annual_cost(self):
        return self.salary

class Manager(Employee):
    def __init__(self, name, salary, bonus):
        super().__init__(name, salary)
        self.bonus = bonus

    def annual_cost(self):
        return self.salary + self.bonus

class BonusPolicy:
    def bonus(self, salary):
        return salary * 0.3

class PayrollProfile:
    def __init__(self, salary, policy):
        self.salary = salary
        self.policy = policy

    def total_compensation(self):
        return self.salary + self.policy.bonus(self.salary)

manager = Manager('Karan', 120000, 15000)
executive_pay = PayrollProfile(12000, BonusPolicy())
print('Manager annual cost:', manager.annual_cost())
print('Executive compensation:', executive_pay.total_compensation())

Guided Practice

Quick MCQ

A ReportService needs to support many export formats and new ones may be added every quarter. Which design is usually better?

  • A) Create one deep inheritance tree for every possible report combination

  • B) Compose the service with interchangeable exporter objects

  • C) Copy the export code into each report class

Correct answer: B

Reflection Questions

  1. Why would composition be easier when notification channels change often?

  2. Give one example where inheritance is still the clearer option.

  3. What is a sign that your inheritance tree is becoming too rigid?

Answer Check
  1. You can replace one notifier object without redesigning the whole class hierarchy.

  2. SavingsAccount extending Account is often reasonable because the subtype still behaves like an account.

  3. You keep adding subclasses just to combine independent features such as payment, shipping, and notifications.

Exercises

Exercise 1

Build an OrderService that composes a TaxStrategy and a DeliveryStrategy.

Exercise 2

Model one case where inheritance is appropriate and justify the is-a relationship in one sentence.

Exercise 3

Take a class hierarchy you already know and ask whether some features should be extracted into helper objects instead of subclasses.

Starter Hint

Look for features that vary independently: pricing, exporting, notifications, recommendation logic, authentication, and storage are often better as composed collaborators.

Key Takeaway

Inheritance says a class becomes a specialized kind of parent. Composition says a class becomes useful by collaborating with other objects.

In modern business systems, composition often wins when behavior changes frequently. That sets up the next notebook on interactions between classes, where those collaborators begin exchanging messages in real workflows.