Когда в ООП следует предпочесть композицию наследованию

Ответ

Композицию следует предпочитать наследованию в большинстве случаев, так как она обеспечивает большую гибкость, слабую связанность (low coupling) и более понятную структуру кода. Этот принцип часто формулируют как "composition over inheritance".

Наследование (отношение "is-a")

Наследование создает жесткую иерархическую связь. Дочерний класс является разновидностью родительского. Это полезно, когда существует четкая таксономия, но может привести к хрупкости базового класса, когда изменения в родителе ломают потомков.

# Неправильное использование наследования
class Engine:
    def start(self):
        print("Engine started")

# Ошибка: Car "is-a" Engine (Машина - это двигатель) - неверно.
class Car(Engine):
    pass

Композиция (отношение "has-a")

Композиция позволяет объекту содержать другие объекты. Класс имеет или использует функциональность другого класса, не становясь его подтипом. Это делает систему более модульной и упрощает замену компонентов.

# Правильное использование композиции
class Engine:
    def start(self):
        print("Engine started")

# Верно: Car "has-a" Engine (У машины есть двигатель).
class Car:
    def __init__(self):
        self.engine = Engine()

    def start(self):
        print("Starting the car...")
        self.engine.start()

Ключевые преимущества композиции:

  • Гибкость: Компоненты можно заменять во время выполнения.
  • Слабая связанность: Изменения в компоненте не затрагивают класс-контейнер, если его интерфейс остается прежним.
  • Простота тестирования: Каждый класс можно тестировать изолированно.