Skip to main content

Section 19.13 Inheritance vs Composition

When designing C++ classes, we now have two tools for code reuse:
Inheritance
is-a : A class is derived from a base class.
Composition or Aggregation
has-a : A class has other classes as members. (We will refer to both of these as just β€œComposition” in this section.)
Choosing the right tool for any particular context is important for writing clean, maintainable code. The language we use to describe the relationships should guide when you use each technique. Inheritance should only be used to model β€œis-a” relationships. In situations where β€œhas-a” is the better description, composition should be preferred.
Say there is an existing class Person and we are making a BankAccount. The BankAccount belongs to a particular person, and we would like to represent this with an explicit relationship. Does it make sense to say a BankAccount is-a Person? Not in the least. This is clearly a spot where inheritance should not be used, even if it would accomplish the technical goal of letting us use existing Person code inside of BankAccount.
Even in situations where inheritance might seem appropriate, composition may still provide a better design. Consider representing various sales transactions. Some are Phone, some are Online, and others are InStore. Each transaction involves payment that is either Cash, Account, or Credit.
Trying to model this with inheritance might lead us to something like:
We need 13 classes to represent the possible orders.
---
config:
  layout: elk
---
classDiagram
    Order <|-- Phone
    Order <|-- Online
    Order <|-- InStore
    Phone <|-- CashPhone
    Phone <|-- AccountPhone
    Phone <|-- CreditPhone
    Online <|-- CashOnline
    Online <|-- AccountOnline
    Online <|-- CreditOnline
    InStore <|-- CashInStore
    InStore <|-- AccountInStore
    InStore <|-- CreditInStore
        
      
Figure 19.13.1. UML for Orders using Inheritance
This is awful. We have 13 classes to represent the different types. And CreditPhone and CreditOnline will probably have code in common. But there is no good place to put that common code. The only parent they have in common is Order and that is not the right place for credit card payment logic.
Multiple inheritance might sound like a solution to that issue. It would let us put common payment logic in a shared parent class for all Credit orders by having a Credit class that they all inherit from. But the class explosion from this design only gets worse. Not only do we have more intermediate classes, we still need classes like CreditPhone to unite the two different aspects (order method and payment method).
We need 17 classes to represent the possible orders.
---
config:
  layout: elk
---
classDiagram
    OrderMethod <|-- Phone
    OrderMethod <|-- Online
    OrderMethod <|-- InStore
    PaymentMethod <|-- Cash
    PaymentMethod <|-- Account
    PaymentMethod <|-- Credit
    Phone <|-- CashPhone
    Phone <|-- AccountPhone
    Phone <|-- CreditPhone
    Online <|-- CashOnline
    Online <|-- AccountOnline
    Online <|-- CreditOnline
    InStore <|-- CashInStore
    InStore <|-- AccountInStore
    InStore <|-- CreditInStore
    Cash <|-- CashPhone
    Account <|-- AccountPhone
    Credit <|-- CreditPhone
    Cash <|-- CashOnline
    Account <|-- AccountOnline
    Credit <|-- CreditOnline
    Cash <|-- CashInStore
    Account <|-- AccountInStore
    Credit <|-- CreditInStore
        
      
Figure 19.13.2. UML for Orders using Multiple Inheritance
Now let’s consider how we might build this with composition. We can have one class Order that has-a instance of PaymentMethod and OrderMethod as members. This way, we can represent all the possible transaction types without an explicit class for each. To make a Order that represent an in store, cash order we would first construct a Cash payment method object and an InStore order method object. We would then use them to construct an Order that had the two other objects as members.
We only need 9 classes to represent any possible order.
---
config:
  layout: elk
---
classDiagram
    OrderMethod <|-- Phone
    OrderMethod <|-- Online
    OrderMethod <|-- InStore
    PaymentMethod <|-- Cash
    PaymentMethod <|-- Account
    PaymentMethod <|-- Credit
    Order o-- OrderMethod
    Order o-- PaymentMethod 
        
      
Figure 19.13.3. UML for Orders using Composition

Insight 19.13.1.

A common piece of advice is β€œfavor composition over inheritance”. Composition tends to allow more flexible relationships and it avoids the class explosion problem. Just because you could think of a design problem in terms of is-a does not mean you have to use inheritance.

Checkpoint 19.13.1.

You have attempted of activities on this page.