Составление (composition) вместо наследования
Наследование говорит «A — это частный случай B». Но часто правильнее сказать «A состоит из B» (has‑a). Тогда вместо наследования используем составление: один объект хранит внутри другие и делегирует им работу.
Команда, состоящая из игроков
class HockeyPlayer:
def __init__(self, name, team, goals=0):
self.name = name
self.team = team
self.goals = goals
def score_goal(self):
self.goals += 1
class Team:
def __init__(self, name):
self.name = name
self.players = [] # составление: команда «содержит» игроков
def add_player(self, player):
# принимаем любой объект, у которого есть .name и .team
if getattr(player, 'team', None) != self.name:
raise ValueError('Игрок должен принадлежать этой команде')
self.players.append(player)
def total_goals(self):
return sum(p.goals for p in self.players)
def lineup(self):
return [p.name for p in self.players]
t = Team('Метеор')
p1 = HockeyPlayer('Иван', 'Метеор')
p2 = HockeyPlayer('Павел', 'Метеор')
t.add_player(p1)
t.add_player(p2)
p1.score_goal(); p1.score_goal()
p2.score_goal()
print(t.lineup())
print(t.total_goals())
['Иван', 'Павел']
3
Когда выбирать составление
- Отношение «has‑a»: команда состоит из игроков, библиотека содержит книги.
- Поведение не нужно переопределять — лучше делегировать: команда суммирует голы, но сами голы забивают игроки.
- Объекты могут независимым образом эволюционировать: меняем внутренности
HockeyPlayer, кодTeamпочти не затрагивается.
Мини‑памятка
- Наследование — когда «is‑a», составление — когда «has‑a».
- Составление снижает связность: объект использует чужие возможности, не становясь их «родителем».
- Составление проще тестировать и переиспользовать.