Перейти к основному содержимому

Составление (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».
  • Составление снижает связность: объект использует чужие возможности, не становясь их «родителем».
  • Составление проще тестировать и переиспользовать.