Skip to main content

Section 8.3 Composition vs. Inheritance: Choosing Wisely

There are two primary ways to reuse functionality between classes: composition and inheritance.
In real-world design scenarios, the choice between these approaches often involves nuanced trade-offs rather than clear-cut decisions:
Inheritance ("is-a" relationship):
  • Advantages:
    • Reuses code automatically without delegation
    • Enables polymorphism through substitutability
    • Creates clean, hierarchical organization
  • Disadvantages:
    • Creates tight coupling that can make future changes difficult
    • Can lead to fragile class hierarchies as depth increases
    • Limited to single inheritance in Java (unlike multiple inheritance in some languages)
    • Implementation details in the superclass may leak into subclasses
  • Example: Monster extends Entity - A monster is a type of entity with specialized behaviors.
Composition ("has-a" relationship):
  • Advantages:
    • Creates more flexible design with looser coupling
    • Allows changing components at runtime
    • Enables combining behaviors from multiple sources
  • Disadvantages:
    • Requires more delegating code (forwarding methods)
    • Class relationships can be less immediately obvious
    • May need more code to implement the same functionality
    • Can become complex if many components need coordination
  • Example: Player has an Inventory object - A player contains but is not a type of inventory.
A good starting question is: "Is this class genuinely a specialized version of another, or does it simply contain or use another object?" However, real designs often involve judgment calls where the lines blur. Many experienced developers adopt the "favor composition over inheritance" guideline while recognizing that inheritance is sometimes the clearer approach.
It’s also common to use both approaches together. For example, you might have a class hierarchy for game entities (inheritance) where each entity contains component objects like weapons or abilities (composition).
Check Your Understanding

Checkpoint 8.3.1.

Which of the following best describes the two primary ways to reuse functionality between classes?
  • Using composition to model "is-a" relationships and inheritance for "has-a" relationships.
  • Using inheritance to derive behavior from a parent class and composition to include functionality from other classes.
  • Using inheritance to derive behavior from a parent class and composition to include functionality from other classes.
  • Copying and pasting code between classes and using interfaces.
  • Writing separate methods for each class to avoid dependencies.

Checkpoint 8.3.2.

In which scenario is composition a better choice than inheritance?
  • When a class needs to share behavior across multiple unrelated classes.
  • When a subclass needs to override most of the superclass methods.
  • When a class represents a "has-a" relationship rather than an "is-a" relationship.
You have attempted of activities on this page.