История вопроса:
Оба термина — композиция и агрегация — пришли из классического объектно-ориентированного проектирования. Они описывают отношения между объектами: "целое–часть", но различаются по степени связанности объектов.
Проблема:
В больших системах важна читаемость и расширяемость кода. Композиция и агрегация помогают выстраивать грамотные зависимости между классами. Важно понимать разницу — иначе можно сделать архитектуру слишком жёсткой или, наоборот, нечеткой.
Решение:
Композиция — это сильная связь «часть-целое». Когда объект содержится только внутри другого объекта и не может существовать вне его. В Python такой объект обычно создают и управляют им внутри класса-"контейнера".
Агрегация — это более слабая связь. Объект-"часть" может существовать отдельно от объекта-"целого" и передаваться в него извне.
Пример кода:
class Engine: def start(self): print("Engine started") class Car: # Композиция: Engine создаем внутри Car def __init__(self): self.engine = Engine() def drive(self): self.engine.start() my_car = Car() my_car.drive() class Wheel: pass class Bicycle: # Агрегация: колёса передаются извне def __init__(self, wheels): self.wheels = wheels w1, w2 = Wheel(), Wheel() bike = Bicycle([w1, w2])
Ключевые особенности:
Если удалить объект-"контейнер" (например, Car) — удалится ли объект Engine?
При композиции, если на объект "часть" больше не ссылается никто, он удалится сборщиком мусора. А при агрегации объект-"часть" может продолжать существовать (на него могут ссылаться ещё другие объекты).
В Python есть специальный синтаксис для агрегации и композиции, как например в других языках?
В Python нет ключевых слов, указывающих явную композицию/агрегацию. Всё зависит от способа создания и жизненного цикла объектов.
Можно ли поменять композицию на агрегацию и наоборот без переписывания логики?
Не всегда. Если логика требует полного контроля и уникальности объекта-"части", простая замена композиции на агрегацию может привести к ошибкам (например, если колёса у разных велосипедов станут общими экземплярами одного класса).
Плюсы:
Негативный кейс: В проекте для каждого автомобиля создавался внешний объект двигателя и отдавался в машину через агрегацию. Однако потом перепутали ссылки на объекты и поменяли движки между машинами произвольно — что привело к путанице и багам.
Плюсы: архитектура казалась гибкой. Минусы: трудно было понять, какой двигатель принадлежит какой машине.
Положительный кейс: В другом проекте двигатели создавались внутри класса Car (композиция); автомобили управляли своими двигателями напрямую, что обеспечило надёжность.
Плюсы: отсутствие утечек и логической путаницы. Минусы: пришлось писать немного больше кода для управления жизненным циклом каждого автомобиля и его деталей.