Что такое композиция в объектно-ориентированном программировании?

Ответ

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

Ключевое отличие от наследования:

  • Наследование — это отношение «is-a» (является). Класс-потомок наследует структуру и поведение родителя.
  • Композиция — отношение «has-a». Класс использует функциональность другого класса, не наследуя его.

Пример на Python:

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

class Car:
    def __init__(self):
        # Композиция: Car содержит (has-a) объект Engine
        self.engine = Engine()

    def start(self):
        # Делегирование вызова методу вложенного объекта
        return self.engine.start()

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

Преимущества композиции:

  • Гибкость: Поведение можно динамически менять, подменяя компоненты во время выполнения.
  • Слабая связанность: Классы зависят от интерфейсов, а не от конкретных реализаций.
  • Избегание проблем наследования: Нет хрупкости базового класса, не нарушается инкапсуляция.
  • Соответствует принципу: Является реализацией принципа «Предпочитай композицию наследованию» (GoF).

Ответ 18+ 🔞

Ну, вот смотри, объясняю на пальцах, а то ты, пизда, опять в наследовании увязнешь, как муха в говне.

Композиция — это когда один класс, такой вот здоровый начальник, не сам всё делает, а содержит в себе других работничков. Отношение тут «has-a», то есть «имеет». У машины есть двигатель, а не машина является двигателем, ёпта. В этом вся и разница.

Наследование — это «is-a», «является». Утка является птицей. Всё, пиздец, теперь ты тащишь за собой все родительские костыли, и если у предка что-то сломается, у тебя тоже всё посыпется. А композиция — она гибкая, хитрая жопа. Не нравится тебе один двигатель — выебал его нахуй и поставил другой, более мощный. И машина даже не заметит, только бы интерфейс подходил.

Вот, смотри на код, не трогай его, он святой:

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

class Car:
    def __init__(self):
        # Композиция, блядь! Car В СЕБЕ СОДЕРЖИТ (has-a) объект Engine.
        # Не наследует, а просто взял и засунул его себе в поля.
        self.engine = Engine()

    def start(self):
        # И теперь просто тупо делегирует работу: "Эй, движок, стартуй, я пасс!"
        return self.engine.start()

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

Видишь? Машина не знает, как стартовать. Она берёт своего работничка engine и говорит: «Слушай, дружок, сделай уже своё дело». И всё работает.

Почему это овердохуища круто:

  • Гибкость, ёбушки-воробушки! Захотел турбину — подменил движок на лету, и поехали. С наследованием так не выйдет, там всё захардкожено в родословной.
  • Слабая связанность, ядрёна вошь! Классы не привязаны друг к другу намертво. Машине похуй, что там внутри движка, лишь бы метод start() был.
  • Избегаешь проблем наследования. Не будет этой хрупкости, когда чихнул базовый класс, а у всех потомков сопли потекли. Каждый компонент живёт в своей песочнице.
  • Это канон, блядь! Самые усатые дядьки из банды «Четверых» (GoF) сказали: «Предпочитай композицию наследованию». Они-то знали, о чём говорили.

Короче, если не хочешь через год поддерживать код, который сам от себя охуел, лепи композицию. Наследование — только когда реально «является», и альтернатив нет. Всё остальное — композиция, и точка, в рот меня чих-пых.