Поиск по блогу

вторник, 14 января 2014 г.

Шаблоны проектирования "является" и "имеет" на примере с пиццерией

Здесь фрагменты кода из книги М.Лутца гл.30, пример с пиццерией - "наследование" , "композиция"... "для этого просто замените имена существительные классами, глаголы – методами, и вы получите первый черновой набросок проекта" (?!?). Однако, мне этот пример показался полезным для того, чтобы поупражнятся с вызовами (видимостью) атрибутов классов и экземпляров классов.

ООП и наследование: взаимосвязи типа «является»

Наш коллектив работников пиццерии можно определить четырьмя классами из файла примера employees.py.
Самый общий класс, Employee, реализует поведение, общее для всех работников, такое как повышение заработной платы (giveRaise) и вывод на экран (repr).

Существует две категории служащих и соответственно, два подкласса, наследующих класс Employee: Chef (повар) и Server (официант). Оба подкласса переопределяют унаследованный метод work, чтобы обеспечить вывод более специализированных сообщений.
Наконец, наш робот по приготовлению пиццы моделируется еще более специализированным классом PizzaRobot, наследующим класс Chef, который в свою очередь наследует класс Employee.

В терминах ООП мы называем такие взаимоотношения «является»: робот является поваром, а повар является служащим. Ниже приводится содержимое файла employees.py:
In [2]:
# employees.py
class Employee:
    def __init__(self, name, salary=0):
        self.name = name
        self.salary = salary
    def giveRaise(self, percent):
        self.salary = self.salary + (self.salary * percent)
    def work(self):
        print self.name, "does stuff"
    def __repr__(self):
        return "<Employee: name=%s, salary=%s>" % (self.name, self.salary)
class Chef(Employee):
    def __init__(self, name):
        Employee.__init__(self, name, 50000)
    def work(self):
        print(self.name, "makes food")
class Server(Employee):
    def __init__(self, name):
        Employee.__init__(self, name, 40000)
    def work(self):
        print(self.name, "interfaces with customer")
class PizzaRobot(Chef):
    def __init__(self, name):
        Chef.__init__(self, name)
    def work(self):
        print(self.name, "makes pizza")
if __name__ == "__main__":
    bob = PizzaRobot('bob') # Создать робота с именем bob
    print(bob) # Вызвать унаследованный метод __repr__
    bob.work() # Выполнить действие, зависящее от типа
    bob.giveRaise(0.20) # Увеличить роботу зарплату на 20%
    print(bob); print()
    for klass in Employee, Chef, Server, PizzaRobot:
        obj = klass(klass.__name__)
        obj.work()
<Employee: name=bob, salary=50000>
('bob', 'makes pizza')
<Employee: name=bob, salary=60000.0>
()
Employee does stuff
('Chef', 'makes food')
('Server', 'interfaces with customer')
('PizzaRobot', 'makes pizza')

Когда выполняется программный код самопроверки, включенный в состав модуля, создается робот по приготовлению пиццы с именем bob, который наследует атрибуты трех классов:
PizzaRobot, Chef и Employee.
Например, при попытке вывести экземпляр bob вызывается метод Employee.repr, а прибавка зарплаты производится методом Employee.giveRaise, потому что этот метод обнаруживается в процессе поиска в дереве наследования именно в этом классе:
В иерархиях классов, подобных этой, обычно можно создавать экземпляры любого класса, а не только того, что находится в самом низу.
Например, в коде самопроверки этого модуля в цикле for создаются экземпляры всех четырех классов, каждый из которых работает по-разному, потому что все они имеют различные методы work.
В действительности эти классы пока лишь имитируют объекты реального мира – в текущей реализации метод work просто выводит сообщение, но позднее его можно расширить так, что он будет выполнять настоящую работу.

ООП и композиция: взаимосвязи типа «имеет»

Вообще составные классы реализуют все свои интерфейсы, управляя работой встраиваемых объектов.
Теперь, когда у нас имеются реализации классов работников, объединим их в коллектив пиццерии и позволим им приступить к работе.
Наша пиццерия – это составной объект:
в нем имеется печь и работники, такие как официанты и повара.
Когда приходит клиент и делает заказ, все компоненты пиццерии начинают действовать –
официант принимает заказ,
повар готовит пиццу и так далее.
Следующий пример (файл pizzashop.py) имитирует все объекты и взаимоотношения между ними:
Строка "#from employees import PizzaRobot, Server" закомментирована потому, что я использовал для блога последовательное выполнение фрагментов кода в iPython notebook (потому импорт не понадобился)
In [3]:
#from employees import PizzaRobot, Server

class Customer:
    def __init__(self, name):
        self.name = name
    def order(self, server):
        print(self.name, "orders from", server)
    def pay(self, server):
        print(self.name, "pays for item to", server)
class Oven:
    def bake(self):
        print("oven bakes")
class PizzaShop:
    def __init__(self):
        self.server = Server('Pat') # Встроить другие объекты
        self.chef = PizzaRobot('Bob') # Робот по имени Bob
        self.oven = Oven()
    def order(self, name):
        customer = Customer(name) # Активизировать другие объекты
        customer.order(self.server) # Клиент делает заказ официанту
        self.chef.work()
        self.oven.bake()
        customer.pay(self.server)
if __name__ == "__main__":
    scene = PizzaShop() # Создать составной объект
    scene.order('Homer') # Имитировать заказ клиента Homer
    print('...')
    scene.order('Shaggy') # Имитировать заказ клиента Shaggy
('Homer', 'orders from', <Employee: name=Pat, salary=40000>)
('Bob', 'makes pizza')
oven bakes
('Homer', 'pays for item to', <Employee: name=Pat, salary=40000>)
...
('Shaggy', 'orders from', <Employee: name=Pat, salary=40000>)
('Bob', 'makes pizza')
oven bakes
('Shaggy', 'pays for item to', <Employee: name=Pat, salary=40000>)

Класс PizzaShop – это контейнер и контроллер – это конструктор, который создает и встраивает экземпляры классов работников, написанные нами в предыдущем разделе, а также экземпляры класса Oven, который определен здесь.
Когда программный код самопроверки этого модуля вызывает метод order класса PizzaShop, встроенным объектам предлагается приступить к выполнению своих обязанностей.
Обратите внимание, что для каждого клиента мы создаем новый экземпляр класса Customer и передаем встроенный объект Server (официант) методам класса Customer (клиент) – клиенты приходят и уходят, а официант остается частью коллектива пиццерии.
Кроме того, обратите внимание, что работники по-прежнему вовлечены во взаимосвязи наследования – композиция и наследование – это взаимодополняющие инструменты.
Если запустить этот модуль, наша пиццерия обслужит два заказа – один от Гомера (Homer) и другой от Шагги (Shaggy)
In []:

Комментариев нет:

Отправить комментарий