Как построить иерархию классов: продолжение
Наследование - не единственный способ создания адаптируемых классов. Вы можете достичь тех же целей (не всегда, но очень часто), используя технику под названием композиция.
Композиция - это процесс создания объекта с использованием других различных объектов. Объекты, используемые в композиции, предоставляют набор желаемых признаков (свойств и/или методов), поэтому мы можем сказать, что они действуют как блоки, используемые для построения более сложной структуры.
Можно сказать, что:
- наследование расширяет возможности класса, добавляя новые компоненты и изменяя существующие; другими словами, полный рецепт содержится внутри самого класса и всех его предков; объект берет все вещи класса и использует их;
- композиция проецирует класс как контейнер, способный хранить и использовать другие объекты (производные от других классов), где каждый из объектов реализует часть поведения желаемого класса.
Давайте проиллюстрируем разницу, используя ранее определенные транспортные средства. Предыдущий подход привел нас к иерархии классов, в которой самый верхний класс знал об общих правилах, используемых при развороте транспортного средства, но не знал, как управлять соответствующими компонентами (колесами или гусеницами).
Подклассы реализовали эту возможность, внедрив специализированные механизмы. Давайте сделаем то же самое, но с использованием композиции. Класс - как в предыдущем примере - знает, как повернуть транспортное средство, но фактический поворот выполняется специализированным объектом, хранящимся в свойстве с именем controller (контроллер)
. Сontroller (контроллер)
может управлять транспортным средством, манипулируя соответствующими частями транспортного средства.
Взгляните в редактор - вот как это может выглядеть.
Есть два класса с именами Tracks
и Wheels
- они знают, как контролировать направление движения автомобиля. Существует также класс с именем Vehicle
, который может использовать любой из доступных контроллеров (два уже определенных или любые другие, определенные в будущем) - сам controller
передается в класс во время инициализации.
Таким образом, способность транспортного средства поворачиваться формируется с использованием внешнего объекта, не реализованного внутри класса Vehicle
.
Другими словами, у нас есть универсальный автомобиль, и мы можем установить на него либо гусеницы, либо колеса.
код дает следующий вывод:
wheels: True True
wheels: True False
tracks: False True
tracks: False False
output
Code
import timeclass Tracks:
def change_direction(self, left, on):
print("tracks: ", left, on)
class Wheels:
def change_direction(self, left, on):
print("wheels: ", left, on)
class Vehicle:
def __init__(self, controller):
self.controller = controller
def turn(self, left):
self.controller.change_direction(left, True)
time.sleep(0.25)
self.controller.change_direction(left, False)
wheeled = Vehicle(Wheels())
tracked = Vehicle(Tracks())
wheeled.turn(True)
tracked.turn(False)