Что такое композиция в ООП и чем она отличается от наследования

Ответ

Композиция — это принцип объектно-ориентированного программирования, при котором один класс включает в себя экземпляры других классов для построения более сложных объектов. Это реализует отношение "has-a" (имеет), где объект-контейнер является "целым", а вложенные объекты — его "частями".

В отличие от наследования (отношение "is-a"), композиция обеспечивает большую гибкость и слабую связанность между компонентами.

Основные преимущества

  • Гибкость: Поведение объекта можно изменять во время выполнения, заменяя его составные части.
  • Инкапсуляция: Внутреннее устройство компонентов скрыто от внешнего мира.
  • Избежание проблем наследования: Помогает обойти проблему "хрупкого базового класса", когда изменения в родительском классе ломают дочерние.

Этот подход соответствует известному принципу проектирования: "предпочитайте композицию наследованию".

Пример: Автомобиль и Двигатель

Автомобиль имеет двигатель. Двигатель — это часть автомобиля, но не его разновидность.

class Engine:
    """Компонент: Двигатель"""
    def start(self):
        print("Двигатель запущен")

class Car:
    """Контейнер: Автомобиль, использующий композицию"""
    def __init__(self):
        # Экземпляр Engine создается внутри Car и полностью им управляется
        self.engine = Engine() 

    def start(self):
        print("Запускаем автомобиль...")
        self.engine.start()

# Создаем автомобиль, который автоматически создает свой двигатель
my_car = Car()
my_car.start()
# Вывод:
# Запускаем автомобиль...
# Двигатель запущен

Ответ 18+ 🔞

А, ну вот, смотри, классическая история, как всегда. Все эти умники с их "наследованием", "полиморфизмом" — а потом сидят и чешут репу, когда их красивая иерархия классов накрывается медным тазом от одного изменения в базовом классе. Пиздец, а не архитектура.

А есть же путь проще, блядь. Композиция, называется. Суть проще пареной репы: ты не пытаешься вывести один класс из другого, крича "я — твой отец!". Нет. Ты просто берёшь и включаешь один объект в другой. Как мотор в машину. Отношение называется "has-a", то есть "имеет". Машина имеет двигатель. А не является двигателем. Чувствуешь разницу, ёпта?

Это даёт тебе овердохуища преимуществ. Хочешь поменять поведение? Да хули там — выковыриваешь старый компонент и впихиваешь новый, прямо во время работы программы. Всё инкапсулировано, ничего никому не видно. И главное — никакой этой ебаной хрупкости, когда чихнул в базовый класс, а у тебя полпрограммы посыпалось. Поэтому умные дядьки и говорят: "предпочитай композицию наследованию". Не просто так, блядь.

Вот, смотри на живом примере. Берём двигатель. Просто двигатель, отдельная сущность.

class Engine:
    """Компонент: Двигатель"""
    def start(self):
        print("Двигатель запущен")

А теперь делаем машину. Она не наследуется от двигателя, блядь, это же бред. Она его имеет. Просто создаёт его у себя внутри и пользуется.

class Car:
    """Контейнер: Автомобиль, использующий композицию"""
    def __init__(self):
        # Вот оно, самое главное! Создаём движок ПРЯМО ЗДЕСЬ.
        # Он наш, мы его породили, мы за него отвечаем. Полная власть.
        self.engine = Engine()

    def start(self):
        print("Запускаем автомобиль...")
        # А тут просто говорим нашему личному движку: "Давай, сука, заводись!"
        self.engine.start()

И всё, пиздец. Работает.

my_car = Car()
my_car.start()
# Вывод:
# Запускаем автомобиль...
# Двигатель запущен

Вот и вся магия. Никаких сложных родственных связей, которые потом не распутаешь. Всё наглядно, как хуй в проруби. Захотел электрокар — выкинул Engine, засунул ElectricMotor. И ни одна строчка кода в других частях программы даже не чихнула. Красота, блядь.