Composition vs Inheritance in Object-Oriented Programming

A comprehensive explanation of composition vs inheritance, including code examples and best practices.

Object-oriented programming (OOP) is a programming paradigm that uses objects and classes to organize code. Two fundamental concepts in OOP are composition and inheritance. Inheritance is a mechanism that allows one class to inherit the properties and behavior of another class. Composition is a mechanism that allows one object to contain another object. The choice between inheritance and composition depends on the relationship between the classes and the complexity of the system.

Code Examples

This example demonstrates inheritance in Python. The Car and Truck classes inherit from the Vehicle class and add their own properties and methods.

class Vehicle:
  def __init__(self, make, model, year):
    self.make = make
    self.model = model
    self.year = year
  def honk(self):
    print('Honk honk')
 
class Car(Vehicle):
  def __init__(self, make, model, year, num_doors):
    super().__init__(make, model, year)
    self.num_doors = num_doors
  def lock_doors(self):
    print('Doors locked')
 
class Truck(Vehicle):
  def __init__(self, make, model, year, bed_size):
    super().__init__(make, model, year)
    self.bed_size = bed_size
  def lift_bed(self):
    print('Bed lifted')

This example demonstrates composition in Python. The Vehicle class contains an Engine object, which is a composition of the make, model, and start method.

class Engine:
  def __init__(self, make, model):
    self.make = make
    self.model = model
  def start(self):
    print('Engine started')
 
class Vehicle:
  def __init__(self, make, model, year):
    self.make = make
    self.model = model
    self.year = year
    self.engine = Engine(make, model)
  def honk(self):
    print('Honk honk')
 
class Car(Vehicle):
  def __init__(self, make, model, year, num_doors):
    super().__init__(make, model, year)
    self.num_doors = num_doors
  def lock_doors(self):
    print('Doors locked')
 
class Truck(Vehicle):
  def __init__(self, make, model, year, bed_size):
    super().__init__(make, model, year)
    self.bed_size = bed_size
  def lift_bed(self):
    print('Bed lifted')

Common Pitfalls

  • Tight coupling between classes can make it difficult to change one class without affecting others.
  • Deep inheritance hierarchies can lead to tight coupling between classes and make it difficult to understand the relationships between classes.

Best Practices

  • Use composition when there is a ‘has-a’ relationship between classes.
  • Use inheritance when there is an ‘is-a’ relationship between classes.
  • Avoid deep inheritance hierarchies and use interfaces and abstract classes to reduce coupling between classes.

See Also