Composition is a fundamental design principle in object-oriented programming (OOP) that represents a “has-a” relationship between classes. This means that one class contains an object of another class, using it to build more complex and versatile functionality. Composition promotes code reuse, modularity, and maintainability, making it a powerful tool in software design.
Composition involves creating complex types by combining objects of other types. Instead of inheriting behavior from a parent class (as in inheritance), a class achieves functionality by including instances of other classes. This relationship indicates that the containing class has an instance of the contained class.
Example: If you have a Car
class that contains objects of Engine
, Wheel
, and Transmission
classes, the Car
class is said to be composed of these parts.
While inheritance is useful for creating a clear hierarchy and reusing code, it can lead to a rigid structure. Composition offers more flexibility by allowing objects to be composed and changed dynamically.
Let’s consider a practical example where a Car
class is composed of Engine
and Wheel
classes.
Example:
class Engine:
def __init__(self, horsepower):
self.horsepower = horsepower
def start(self):
print("Engine started")
class Wheel:
def __init__(self, size):
self.size = size
def rotate(self):
print("Wheel is rotating")
class Car:
def __init__(self, horsepower, wheel_size):
self.engine = Engine(horsepower)
self.wheels = [Wheel(wheel_size) for _ in range(4)]
def start(self):
self.engine.start()
for wheel in self.wheels:
wheel.rotate()
# Usage
my_car = Car(150, 16)
my_car.start()
In this example, the Car
class contains an Engine
object and a list of Wheel
objects, illustrating the “has-a” relationship. The Car
class can start its engine and rotate its wheels, leveraging the functionality provided by the composed objects.
Consider a scenario where a Library
class is composed of Book
and Librarian
classes.
Example:
class Book:
def __init__(self, title, author):
self.title = title
self.author = author
def info(self):
return f"{self.title} by {self.author}"
class Librarian:
def __init__(self, name):
self.name = name
def assist(self):
print(f"Librarian {self.name} is assisting you")
class Library:
def __init__(self):
self.books = []
self.librarian = Librarian("Jane Doe")
def add_book(self, book):
self.books.append(book)
print(f"Book '{book.title}' added to the library")
def list_books(self):
for book in self.books:
print(book.info())
def assist_patron(self):
self.librarian.assist()
# Usage
library = Library()
book1 = Book("1984", "George Orwell")
book2 = Book("To Kill a Mockingbird", "Harper Lee")
library.add_book(book1)
library.add_book(book2)
library.list_books()
library.assist_patron()
In this example, the Library
class is composed of a list of Book
objects and a Librarian
object. This demonstrates how composition can be used to create complex structures and relationships within a system.
Composition is a powerful design principle in OOP that enables building complex functionality through the aggregation of simpler objects. It promotes code reuse, flexibility, and maintainability by allowing objects to be composed, reused, and modified independently. Understanding and leveraging composition can lead to more modular and maintainable software designs.