Design Patterns Ru PDF
Design Patterns Ru PDF
ПАТТЕРНЫ
ПРОЕКТИРОВАНИЯ
v2020-2.23
Содержание
Содержание............................................................................................. 4
Как читать эту книгу.............................................................................. 6
ВВЕДЕНИЕ В ООП................................................................................... 7
Вспоминаем ООП........................................................................... 8
Краеугольные камни ООП....................................................... 13
Отношения между объектами................................................ 20
ОСНОВЫ ПАТТЕРНОВ..........................................................................26
Что такое паттерн? ..................................................................... 27
Зачем знать паттерны? ............................................................. 31
ПРИНЦИПЫ ПРОЕКТИРОВАНИЯ......................................................32
Качества хорошей архитектуры ........................................... 33
Базовые принципы проектирования ....................................37
§ Инкапсулируйте то, что меняется............................ 38
§ Программируйте на уровне интерфейса ............. 42
§ Предпочитайте композицию наследованию...... 47
Принципы SOLID .........................................................................51
§ S: Принцип единственной ответственности........ 52
§ O: Принцип открытости/закрытости....................... 54
§ L: Принцип подстановки Лисков ............................. 57
§ I: Принцип разделения интерфейса....................... 63
§ D: Принцип инверсии зависимостей ..................... 66
[email protected] (#11242)
5 Содержание #11242
[email protected] (#11242)
6 Как читать эту книгу? #11242
[email protected] (#11242)
#11242
ВВЕДЕНИЕ В
ООП
[email protected] (#11242)
8 ВВЕДЕНИЕ В ООП / Вспоминаем ООП #11242
Вспоминаем ООП
Объектно-ориентированное программирование — это мето-
дология программирования, в которой все важные вещи
представлены объектами, каждый из которых является
экземпляром определенного класса, а классы образуют
иерархию наследования.
Объекты, классы
Вы любите котиков? Надеюсь, да, потому что я попытаюсь
объяснить все эти вещи на примерах с котами.
[email protected] (#11242)
9 ВВЕДЕНИЕ В ООП / Вспоминаем ООП #11242
[email protected] (#11242)
10 ВВЕДЕНИЕ В ООП / Вспоминаем ООП #11242
Иерархии классов
Идём дальше. У вашего соседа есть собака Жучка. Как
известно, и собаки, и коты имеют много общего: имя, пол,
возраст, цвет есть не только у котов, но и у собак. Да и
бегать, дышать, спать и есть могут не только коты. Полу-
чается, эти свойства и поведения присущи общему классу
Животных .
[email protected] (#11242)
11 ВВЕДЕНИЕ В ООП / Вспоминаем ООП #11242
[email protected] (#11242)
12 ВВЕДЕНИЕ В ООП / Вспоминаем ООП #11242
[email protected] (#11242)
13 ВВЕДЕНИЕ В ООП / Краеугольные камни ООП #11242
Абстракция
Когда вы пишете программу, используя ООП, вы представ-
ляете её части через объекты реального мира. Но объекты
в программе не повторяют в точности их реальные аналоги,
да и это редко бывает нужно. Вместо этого объекты програм-
мы всего лишь моделируют свойства и поведения реальных
объектов, важные в конкретном контексте, а остальные —
игнорируют.
[email protected] (#11242)
14 ВВЕДЕНИЕ В ООП / Краеугольные камни ООП #11242
Инкапсуляция
Когда вы заводите автомобиль, вам достаточно повернуть
ключи зажигания или нажать кнопку. Вам не нужно вруч-
ную соединять провода под капотом, поворачивать коленча-
тый вал и поршни, запуская такт двигателя. Все эти детали
скрыты под капотом автомобиля. Вам доступен только про-
[email protected] (#11242)
15 ВВЕДЕНИЕ В ООП / Краеугольные камни ООП #11242
[email protected] (#11242)
16 ВВЕДЕНИЕ В ООП / Краеугольные камни ООП #11242
[email protected] (#11242)
17 ВВЕДЕНИЕ В ООП / Краеугольные камни ООП #11242
Наследование
Наследование — это возможность создания новых классов
на основе существующих. Главная польза от наследования —
повторное использование существующего кода. Расплата за
наследование проявляется в том, что подклассы всегда сле-
дуют интерфейсу родительского класса. Вы не можете
исключить из подкласса метод, объявленный в его родителе.
[email protected] (#11242)
18 ВВЕДЕНИЕ В ООП / Краеугольные камни ООП #11242
Полиморфизм
Вернёмся к примерам с животными. Практически все
Животные умеют издавать звуки, поэтому мы можем сде-
лать их общий метод издавания звуков абстрактным. Все
подклассы должны будут переопределить и реализовать
такой метод по-своему.
[email protected] (#11242)
19 ВВЕДЕНИЕ В ООП / Краеугольные камни ООП #11242
1 bag = [new
new Cat(), new Dog()];
2
3 foreach (Animal a : bag)
4 a.makeSound()
5
6 // Meow!
7 // Bark!
[email protected] (#11242)
20 ВВЕДЕНИЕ В ООП / Отношения между объектами #11242
Зависимость
[email protected] (#11242)
21 ВВЕДЕНИЕ В ООП / Отношения между объектами #11242
Ассоциация
[email protected] (#11242)
22 ВВЕДЕНИЕ В ООП / Отношения между объектами #11242
1 class Professor is
2 field Student student
3 // ...
4 method teach(Course c) is
5 // ...
6 this
this.student.remember(c.getKnowledge())
Агрегация
[email protected] (#11242)
23 ВВЕДЕНИЕ В ООП / Отношения между объектами #11242
Композиция
[email protected] (#11242)
24 ВВЕДЕНИЕ В ООП / Отношения между объектами #11242
Общая картина
Теперь, когда мы знаем о всех видах отношений, можно
взглянуть как они относятся друг к другу. Это избавит вас от
путаницы и вопросов вроде «чем агрегация отличается от
композиции» и «является ли наследование зависимостью».
[email protected] (#11242)
25 ВВЕДЕНИЕ В ООП / Отношения между объектами #11242
[email protected] (#11242)
#11242
ОСНОВЫ
ПАТТЕРНОВ
[email protected] (#11242)
27 ОСНОВЫ ПАТТЕРНОВ / Что такое паттерн? #11242
[email protected] (#11242)
28 ОСНОВЫ ПАТТЕРНОВ / Что такое паттерн? #11242
Классификация паттернов
Паттерны отличаются по уровню сложности, детализации и
охвата проектируемой системы. Проводя аналогию со стро-
ительством, вы можете повысить безопасность перекрёстка,
поставив светофор, а можете заменить перекрёсток целой
автомобильной развязкой с подземными переходами.
[email protected] (#11242)
29 ОСНОВЫ ПАТТЕРНОВ / Что такое паттерн? #11242
[email protected] (#11242)
30 ОСНОВЫ ПАТТЕРНОВ / Что такое паттерн? #11242
[email protected] (#11242)
31 ОСНОВЫ ПАТТЕРНОВ / Зачем знать паттерны? #11242
[email protected] (#11242)
#11242
ПРИНЦИПЫ
ПРОЕКТИРОВАНИЯ
[email protected] (#11242)
33 ПРИНЦИПЫ ПРОЕКТИРОВАНИЯ / Качества хорошей архитектуры #11242
[email protected] (#11242)
34 ПРИНЦИПЫ ПРОЕКТИРОВАНИЯ / Повторное использование кода #11242
1
Приведу цитату Эриха Гаммы , одного из первооткрывате-
лей паттернов, о повторном использовании кода и роли пат-
тернов в нём.
[email protected] (#11242)
35 ПРИНЦИПЫ ПРОЕКТИРОВАНИЯ / Расширяемость #11242
Есть ещё средний уровень. Это то, где я вижу паттерны. Пат-
терны проектирования и меньше по объёму кода, и более
абстрактны, чем фреймворки. Они, на самом деле, просто
описание того, как парочка классов относится и взаимодей-
ствует друг с другом. Уровень повторного использования
повышается, когда вы двигаетесь в направлении от конкрет-
ных классов к паттернам, а затем к фреймворкам.
„
паттерны позволяют вам повторно использовать идеи и кон-
цепции в отрыве от конкретного кода.
Расширяемость
Изменения часто называют главным врагом программиста.
[email protected] (#11242)
36 ПРИНЦИПЫ ПРОЕКТИРОВАНИЯ / Расширяемость #11242
[email protected] (#11242)
37 Базовые принципы #11242
Базовые принципы
проектирования
Что такое хороший дизайн? По каким критериям его оцени-
вать и каких правил придерживаться при разработке? Как
обеспечить достаточный уровень гибкости, связанности,
управляемости, стабильности и понятности кода?
[email protected] (#11242)
38 Базовые принципы / Инкапсулируйте то, что меняется #11242
[email protected] (#11242)
39 Базовые принципы / Инкапсулируйте то, что меняется #11242
1 method getOrderTotal(order) is
2 total = 0
3 foreach item in order.lineItems
4 total += item.price * item.quantity
5
6 if (order.country == "US")
7 total += total * 0.07 // US sales tax
8 else if (order.country == "EU")
9 total += total * 0.20 // European VAT
10
11 return total
[email protected] (#11242)
40 Базовые принципы / Инкапсулируйте то, что меняется #11242
1 method getOrderTotal(order) is
2 total = 0
3 foreach item in order.lineItems
4 total += item.price * item.quantity
5
6 total += total * getTaxAmount(order.country)
7
8 return total
9
10 method getTaxAmount(country) is
11 if (country == "US")
12 return 0.07 // US sales tax
13 else if (country == "EU")
14 return 0.20 // European VAT
15 else
16 return 0
[email protected] (#11242)
41 Базовые принципы / Инкапсулируйте то, что меняется #11242
[email protected] (#11242)
42 Базовые принципы / Программируйте на уровне интерфейса #11242
[email protected] (#11242)
43 Базовые принципы / Программируйте на уровне интерфейса #11242
Пример
[email protected] (#11242)
44 Базовые принципы / Программируйте на уровне интерфейса #11242
[email protected] (#11242)
45 Базовые принципы / Программируйте на уровне интерфейса #11242
[email protected] (#11242)
46 Базовые принципы / Программируйте на уровне интерфейса #11242
[email protected] (#11242)
47 Базовые принципы / Предпочитайте композицию наследованию #11242
Предпочитайте композицию
наследованию
Наследование — это самый простой и быстрый способ
повторного использования кода между классами. У вас есть
два класса с дублирующимся кодом. Создайте для них
общий базовый класс и перенесите в него общее поведение.
Что может быть проще?
[email protected] (#11242)
48 Базовые принципы / Предпочитайте композицию наследованию #11242
Пример
[email protected] (#11242)
49 Базовые принципы / Предпочитайте композицию наследованию #11242
[email protected] (#11242)
50 Базовые принципы / Предпочитайте композицию наследованию #11242
[email protected] (#11242)
51 Принципы SOLID #11242
Принципы SOLID
Рассмотрим ещё пять принципов проектирования, которые
известны как SOLID. Эти принципы были впервые изложе-
ны Робертом Мартином в книге Agile Software Development,
1
Principles, Patterns, and Practices .
[email protected] (#11242)
52 Принципы SOLID / S: Принцип единственной ответственности #11242
[email protected] (#11242)
53 Принципы SOLID / S: Принцип единственной ответственности #11242
Пример
Класс Employee имеет сразу несколько причин для изме-
нения. Первая связана с основной задачей класса — управ-
лением данными сотрудника. Но есть и вторая: изменения,
связанные с форматированием отчёта для печати, будут
затрагивать класс сотрудников.
[email protected] (#11242)
54 Принципы SOLID / O: Принцип открытости/закрытости #11242
O pen/Closed Principle
Принцип открытости/закрытости
[email protected] (#11242)
55 Принципы SOLID / O: Принцип открытости/закрытости #11242
Пример
Класс заказов имеет метод расчёта стоимости доставки,
причём способы доставки «зашиты» непосредственно в сам
метод. Если вам нужно будет добавить новый способ достав-
ки, то придётся трогать весь класс Order .
ДО: код класса заказа нужно будет изменять при добавлении нового
способа доставки.
[email protected] (#11242)
56 Принципы SOLID / O: Принцип открытости/закрытости #11242
[email protected] (#11242)
57 Принципы SOLID / L: Принцип подстановки Лисков #11242
[email protected] (#11242)
58 Принципы SOLID / L: Принцип подстановки Лисков #11242
[email protected] (#11242)
59 Принципы SOLID / L: Принцип подстановки Лисков #11242
[email protected] (#11242)
60 Принципы SOLID / L: Принцип подстановки Лисков #11242
[email protected] (#11242)
61 Принципы SOLID / L: Принцип подстановки Лисков #11242
Пример
Чтобы закрыть тему принципа подстановки, давайте рас-
смотрим пример неудачной иерархии классов документов.
[email protected] (#11242)
62 Принципы SOLID / L: Принцип подстановки Лисков #11242
[email protected] (#11242)
63 Принципы SOLID / I: Принцип разделения интерфейса #11242
[email protected] (#11242)
64 Принципы SOLID / I: Принцип разделения интерфейса #11242
Пример
Представьте библиотеку для работы с облачными провайде-
рами. В первой версии она поддерживала только Amazon,
имеющий полный набор облачных услуг. Исходя из них и
проектировался интерфейс будущих классов.
[email protected] (#11242)
65 Принципы SOLID / I: Принцип разделения интерфейса #11242
[email protected] (#11242)
66 Принципы SOLID / D: Принцип инверсии зависимостей #11242
[email protected] (#11242)
67 Принципы SOLID / D: Принцип инверсии зависимостей #11242
Пример
В этом примере высокоуровневый класс формирования
бюджетных отчётов напрямую использует класс базы дан-
ных для загрузки и сохранения своей информации.
[email protected] (#11242)
68 Принципы SOLID / D: Принцип инверсии зависимостей #11242
[email protected] (#11242)
#11242
КАТАЛОГ
ПАТТЕРНОВ
[email protected] (#11242)
70 Порождающие паттерны #11242
Порождающие паттерны
Эти паттерны отвечают за удобное и безопасное создание
новых объектов или даже целых семейств объектов.
Фабричный метод
Factory Method
Абстрактная фабрика
Abstract Factory
[email protected] (#11242)
71 Порождающие паттерны #11242
Строитель
Builder
Прототип
Prototype
Одиночка
Singleton
[email protected] (#11242)
72 Порождающие паттерны / Фабричный метод #11242
ФАБРИЧНЫЙ
МЕТОД
Также известен как: Виртуальный конструктор, Factory Method
[email protected] (#11242)
73 Порождающие паттерны / Фабричный метод #11242
Проблема
Представьте, что вы создаёте программу управления гру-
зовыми перевозками. Сперва вы рассчитываете перевозить
товары только на автомобилях. Поэтому весь ваш код рабо-
тает с объектами класса Грузовик .
Добавить новый класс не так-то просто, если весь код уже завязан
на конкретные классы.
[email protected] (#11242)
74 Порождающие паттерны / Фабричный метод #11242
Решение
Паттерн Фабричный метод предлагает создавать объекты не
напрямую, используя оператор new , а через вызов особого
фабричного метода. Не пугайтесь, объекты всё равно будут
создаваться при помощи new , но делать это будет фабрич-
ный метод.
[email protected] (#11242)
75 Порождающие паттерны / Фабричный метод #11242
[email protected] (#11242)
76 Порождающие паттерны / Фабричный метод #11242
[email protected] (#11242)
77 Порождающие паттерны / Фабричный метод #11242
Структура
[email protected] (#11242)
78 Порождающие паттерны / Фабричный метод #11242
Псевдокод
В этом примере Фабричный метод помогает создавать
кросс-платформенные элементы интерфейса, не привязы-
вая основной код программы к конкретным классам
элементов.
[email protected] (#11242)
79 Порождающие паттерны / Фабричный метод #11242
[email protected] (#11242)
80 Порождающие паттерны / Фабричный метод #11242
5 method onClick(f)
6
7 class WindowsButton implements Button is
8 method render(a, b) is
9 // Отрисовать кнопку в стиле Windows.
10 method onClick(f) is
11 // Навесить на кнопку обработчик событий Windows.
12
13 class HTMLButton implements Button is
14 method render(a, b) is
15 // Вернуть HTML-код кнопки.
16 method onClick(f) is
17 // Навесить на кнопку обработчик события браузера.
18
19
20 // Базовый класс фабрики. Заметьте, что "фабрика" — это всего
21 // лишь дополнительная роль для класса. Скорее всего, он уже
22 // имеет какую-то бизнес-логику, в которой требуется создание
23 // разнообразных продуктов.
24 class Dialog is
25 method render() is
26 // Чтобы использовать фабричный метод, вы должны
27 // убедиться в том, что эта бизнес-логика не зависит от
28 // конкретных классов продуктов. Button — это общий
29 // интерфейс кнопок, поэтому все хорошо.
30 Button okButton = createButton()
31 okButton.onClick(closeDialog)
32 okButton.render()
33
34 // Мы выносим весь код создания продуктов в особый метод,
35 // который назвают "фабричным".
36 abstract method createButton():Button
[email protected] (#11242)
81 Порождающие паттерны / Фабричный метод #11242
[email protected] (#11242)
82 Порождающие паттерны / Фабричный метод #11242
Применимость
Когда заранее неизвестны типы и зависимости объектов, с
которыми должен работать ваш код.
[email protected] (#11242)
83 Порождающие паттерны / Фабричный метод #11242
[email protected] (#11242)
84 Порождающие паттерны / Фабричный метод #11242
Шаги реализации
1. Приведите все создаваемые продукты к общему
интерфейсу.
[email protected] (#11242)
85 Порождающие паттерны / Фабричный метод #11242
[email protected] (#11242)
86 Порождающие паттерны / Фабричный метод #11242
Преимущества и недостатки
Избавляет класс от привязки к конкретным классам
продуктов.
Выделяет код производства продуктов в одно место, упро-
щая поддержку кода.
Упрощает добавление новых продуктов в программу.
[email protected] (#11242)
87 Порождающие паттерны / Фабричный метод #11242
[email protected] (#11242)
88 Порождающие паттерны / Абстрактная фабрика #11242
АБСТРАКТНАЯ
ФАБРИКА
Также известен как: Abstract Factory
[email protected] (#11242)
89 Порождающие паттерны / Абстрактная фабрика #11242
Проблема
Представьте, что вы пишете симулятор мебельного магази-
на. Ваш код содержит:
+ Столик .
[email protected] (#11242)
90 Порождающие паттерны / Абстрактная фабрика #11242
Решение
Для начала паттерн Абстрактная фабрика предлагает выде-
лить общие интерфейсы для отдельных продуктов, состав-
ляющих семейства. Так, все вариации кресел получат общий
интерфейс Кресло , все диваны реализуют интерфейс
Диван и так далее.
[email protected] (#11242)
91 Порождающие паттерны / Абстрактная фабрика #11242
[email protected] (#11242)
92 Порождающие паттерны / Абстрактная фабрика #11242
[email protected] (#11242)
93 Порождающие паттерны / Абстрактная фабрика #11242
Структура
[email protected] (#11242)
94 Порождающие паттерны / Абстрактная фабрика #11242
[email protected] (#11242)
95 Порождающие паттерны / Абстрактная фабрика #11242
Псевдокод
В этом примере Абстрактная фабрика создаёт кросс-плат-
форменные элементы интерфейса и следит за тем, чтобы
они соответствовали выбранной операционной системе.
[email protected] (#11242)
96 Порождающие паттерны / Абстрактная фабрика #11242
[email protected] (#11242)
97 Порождающие паттерны / Абстрактная фабрика #11242
[email protected] (#11242)
98 Порождающие паттерны / Абстрактная фабрика #11242
[email protected] (#11242)
99 Порождающие паттерны / Абстрактная фабрика #11242
Применимость
Когда бизнес-логика программы должна работать с разны-
ми видами связанных друг с другом продуктов, не завися от
конкретных классов продуктов.
[email protected] (#11242)
100 Порождающие паттерны / Абстрактная фабрика #11242
Шаги реализации
1. Создайте таблицу соотношений типов продуктов к вариаци-
ям семейств продуктов.
[email protected] (#11242)
101 Порождающие паттерны / Абстрактная фабрика #11242
Преимущества и недостатки
Гарантирует сочетаемость создаваемых продуктов.
[email protected] (#11242)
102 Порождающие паттерны / Абстрактная фабрика #11242
[email protected] (#11242)
103 Порождающие паттерны / Строитель #11242
СТРОИТЕЛЬ
Также известен как: Builder
[email protected] (#11242)
104 Порождающие паттерны / Строитель #11242
Проблема
Представьте сложный объект, требующий кропотливой
пошаговой инициализации множества полей и вложенных
объектов. Код инициализации таких объектов обычно спря-
тан внутри монструозного конструктора с десятком парамет-
ров. Либо ещё хуже — распылён по всему клиентскому коду.
[email protected] (#11242)
105 Порождающие паттерны / Строитель #11242
[email protected] (#11242)
106 Порождающие паттерны / Строитель #11242
Решение
Паттерн Строитель предлагает вынести конструирование
объекта за пределы его собственного класса, поручив это
дело отдельным объектам, называемым строителями.
[email protected] (#11242)
107 Порождающие паттерны / Строитель #11242
[email protected] (#11242)
108 Порождающие паттерны / Строитель #11242
Директор
[email protected] (#11242)
109 Порождающие паттерны / Строитель #11242
Структура
[email protected] (#11242)
110 Порождающие паттерны / Строитель #11242
Псевдокод
В этом примере Строитель используется для пошагового
конструирования автомобилей, а также технических руко-
водств к ним.
[email protected] (#11242)
111 Порождающие паттерны / Строитель #11242
[email protected] (#11242)
112 Порождающие паттерны / Строитель #11242
[email protected] (#11242)
113 Порождающие паттерны / Строитель #11242
8 class Manual is
9 // Руководство пользователя для данной конфигурации
10 // автомобиля.
11
12
13 // Интерфейс строителя объявляет все возможные этапы и шаги
14 // конфигурации продукта.
15 interface Builder is
16 method reset()
17 method setSeats(...)
18 method setEngine(...)
19 method setTripComputer(...)
20 method setGPS(...)
21
22 // Все конкретные строители реализуют общий интерфейс по-своему.
23 class CarBuilder implements Builder is
24 private field car:Car
25 method reset()
26 // Поместить новый объект Car в поле "car".
27 method setSeats(...) is
28 // Установить указанное количество сидений.
29 method setEngine(...) is
30 // Установить поданный двигатель.
31 method setTripComputer(...) is
32 // Установить поданную систему навигации.
33 method setGPS(...) is
34 // Установить или снять GPS.
35 method getResult():Car is
36 // Вернуть текущий объект автомобиля.
37
38 // В отличие от других порождающих паттернов, где продукты
39 // должны быть частью одной иерархии классов или следовать
[email protected] (#11242)
114 Порождающие паттерны / Строитель #11242
[email protected] (#11242)
115 Порождающие паттерны / Строитель #11242
Применимость
Когда вы хотите избавиться от «телескопического
конструктора».
[email protected] (#11242)
116 Порождающие паттерны / Строитель #11242
1 class Pizza {
2 Pizza(int size) { ... }
3 Pizza(int size, boolean cheese) { ... }
4 Pizza(int size, boolean cheese, boolean pepperoni) { ... }
5 // ...
[email protected] (#11242)
117 Порождающие паттерны / Строитель #11242
Шаги реализации
1. Убедитесь в том, что создание разных представлений объек-
та можно свести к общим шагам.
[email protected] (#11242)
118 Порождающие паттерны / Строитель #11242
Преимущества и недостатки
Позволяет создавать продукты пошагово.
[email protected] (#11242)
119 Порождающие паттерны / Строитель #11242
[email protected] (#11242)
120 Порождающие паттерны / Прототип #11242
ПРОТОТИП
Также известен как: Клон, Prototype
[email protected] (#11242)
121 Порождающие паттерны / Прототип #11242
Проблема
У вас есть объект, который нужно скопировать. Как это сде-
лать? Нужно создать пустой объект такого же класса, а затем
поочерёдно скопировать значения всех полей из старого
объекта в новый.
[email protected] (#11242)
122 Порождающие паттерны / Прототип #11242
Решение
Паттерн Прототип поручает создание копий самим копи-
руемым объектам. Он вводит общий интерфейс для всех
объектов, поддерживающих клонирование. Это позволяет
копировать объекты, не привязываясь к их конкретным
классам. Обычно такой интерфейс имеет всего один метод
clone .
[email protected] (#11242)
123 Порождающие паттерны / Прототип #11242
Аналогия из жизни
В промышленном производстве прототипы создаются перед
основной партией продуктов для проведения всевозмож-
ных испытаний. При этом прототип не участвует в последу-
ющем производстве, отыгрывая пассивную роль.
[email protected] (#11242)
124 Порождающие паттерны / Прототип #11242
Структура
Базовая реализация
[email protected] (#11242)
125 Порождающие паттерны / Прототип #11242
[email protected] (#11242)
126 Порождающие паттерны / Прототип #11242
Псевдокод
В этом примере Прототип позволяет производить точные
копии объектов геометрических фигур, не привязываясь к
их классам.
[email protected] (#11242)
127 Порождающие паттерны / Прототип #11242
1 // Базовый прототип.
2 abstract class Shape is
3 field X: int
4 field Y: int
5 field color: string
6
7 // Обычный конструктор.
8 constructor Shape() is
9 // ...
10
11 // Конструктор прототипа.
12 constructor Shape(source: Shape) is
13 this
this()
14 this
this.X = source.X
15 this
this.Y = source.Y
16 this
this.color = source.color
17
18 // Результатом операции клонирования всегда будет объект из
19 // иерархии классов Shape.
20 abstract method clone():Shape
21
22 // Конкретный прототип. Метод клонирования создаёт новый объект
23 // текущего класса, передавая в его конструктор ссылку на
24 // собственный объект. Благодаря этому операция клонирования
25 // получается атомарной — пока не выполнится конструктор, нового
26 // объекта ещё не существует. Но как только конструктор завершит
[email protected] (#11242)
128 Порождающие паттерны / Прототип #11242
[email protected] (#11242)
129 Порождающие паттерны / Прототип #11242
59 constructor Application() is
60 Circle circle = new Circle()
61 circle.X = 10
62 circle.Y = 10
63 circle.radius = 20
64 shapes.add(circle)
65
66 Circle anotherCircle = circle.clone()
67 shapes.add(anotherCircle)
68 // anotherCircle будет содержать точную копию circle.
69
70 Rectangle rectangle = new Rectangle()
71 rectangle.width = 10
72 rectangle.height = 20
73 shapes.add(rectangle)
74
75 method businessLogic() is
76 // Плюс Прототипа в том, что вы можете клонировать набор
77 // объектов, не зная их конкретные классы.
78 Array shapesCopy = new Array of Shapes.
79
80 // Например, мы не знаем, какие конкретно объекты
81 // находятся внутри массива shapes, так как он объявлен
82 // с типом Shape. Но благодаря полиморфизму, мы можем
83 // клонировать все объекты «вслепую». Будет выполнен
84 // метод clone того класса, которым является этот
85 // объект.
86 foreach (s in shapes) do
87 shapesCopy.add(s.clone())
88
89 // Переменная shapesCopy будет содержать точные копии
90 // элементов массива shapes.
[email protected] (#11242)
130 Порождающие паттерны / Прототип #11242
Применимость
Когда ваш код не должен зависеть от классов копируемых
объектов.
[email protected] (#11242)
131 Порождающие паттерны / Прототип #11242
Шаги реализации
1. Создайте интерфейс прототипов с единственным методом
clone . Если у вас уже есть иерархия продуктов, метод кло-
нирования можно объявить непосредственно в каждом из
её классов.
[email protected] (#11242)
132 Порождающие паттерны / Прототип #11242
Преимущества и недостатки
Позволяет клонировать объекты, не привязываясь к их кон-
кретным классам.
Меньше повторяющегося кода инициализации объектов.
[email protected] (#11242)
133 Порождающие паттерны / Прототип #11242
[email protected] (#11242)
134 Порождающие паттерны / Одиночка #11242
ОДИНОЧКА
Также известен как: Singleton
[email protected] (#11242)
135 Порождающие паттерны / Одиночка #11242
Проблема
Одиночка решает сразу две проблемы, нарушая принцип
единственной ответственности класса.
[email protected] (#11242)
136 Порождающие паттерны / Одиночка #11242
Решение
Все реализации одиночки сводятся к тому, чтобы скрыть
конструктор по умолчанию и создать публичный статиче-
ский метод, который и будет контролировать жизненный
цикл объекта-одиночки.
[email protected] (#11242)
137 Порождающие паттерны / Одиночка #11242
Аналогия из жизни
Правительство государства — хороший пример одиночки. В
государстве может быть только одно официальное прави-
тельство. Вне зависимости от того, кто конкретно заседает в
правительстве, оно имеет глобальную точку доступа «Прави-
тельство страны N».
Структура
[email protected] (#11242)
138 Порождающие паттерны / Одиночка #11242
Псевдокод
В этом примере роль Одиночки отыгрывает класс подклю-
чения к базе данных.
[email protected] (#11242)
139 Порождающие паттерны / Одиночка #11242
21 if (Database.instance == null
null) then
22 acquireThreadLock() and then
23 // На всякий случай ещё раз проверим, не был ли
24 // объект создан другим потоком, пока текущий
25 // ждал освобождения блокировки.
26 if (Database.instance == null
null) then
27 Database.instance = new Database()
28 return Database.instance
29
30 // Наконец, любой класс одиночки должен иметь какую-то
31 // полезную функциональность, которую клиенты будут
32 // запускать через полученный объект одиночки.
33 public method query(sql) is
34 // Все запросы к базе данных будут проходить через этот
35 // метод. Поэтому имеет смысл поместить сюда какую-то
36 // логику кеширования.
37 // ...
38
39 class Application is
40 method main() is
41 Database foo = Database.getInstance()
42 foo.query("SELECT ...")
43 // ...
44 Database bar = Database.getInstance()
45 bar.query("SELECT ...")
46 // Переменная "bar" содержит тот же объект, что и
47 // переменная "foo".
[email protected] (#11242)
140 Порождающие паттерны / Одиночка #11242
Применимость
Когда в программе должен быть единственный экземпляр
какого-то класса, доступный всем клиентам (например,
общий доступ к базе данных из разных частей программы).
Шаги реализации
1. Добавьте в класс приватное статическое поле, которое будет
содержать одиночный объект.
[email protected] (#11242)
141 Порождающие паттерны / Одиночка #11242
Преимущества и недостатки
Гарантирует наличие единственного экземпляра класса.
[email protected] (#11242)
142 Порождающие паттерны / Одиночка #11242
[email protected] (#11242)
143 Структурные паттерны #11242
Структурные паттерны
Эти паттерны отвечают за построение удобных в поддержке
иерархий классов.
Адаптер
Adapter
Мост
Bridge
[email protected] (#11242)
144 Структурные паттерны #11242
Компоновщик
Composite
Декоратор
Decorator
Фасад
Facade
[email protected] (#11242)
145 Структурные паттерны #11242
Легковес
Flyweight
Заместитель
Proxy
[email protected] (#11242)
146 Структурные паттерны / Адаптер #11242
АДАПТЕР
Также известен как: Wrapper, Обёртка, Adapter
[email protected] (#11242)
147 Структурные паттерны / Адаптер #11242
Проблема
Представьте, что вы делаете приложение для торговли на
бирже. Ваше приложение скачивает биржевые котировки
из нескольких источников в XML, а затем рисует красивые
графики.
[email protected] (#11242)
148 Структурные паттерны / Адаптер #11242
Решение
Вы можете создать адаптер. Это объект-переводчик, кото-
рый трансформирует интерфейс или данные одного объекта
в такой вид, чтобы он стал понятен другому объекту.
[email protected] (#11242)
149 Структурные паттерны / Адаптер #11242
Аналогия из жизни
Когда вы в первый раз летите за границу, вас может ждать
сюрприз при попытке зарядить ноутбук. Стандарты розеток
в разных странах отличаются.
[email protected] (#11242)
150 Структурные паттерны / Адаптер #11242
Структура
Адаптер объектов
[email protected] (#11242)
151 Структурные паттерны / Адаптер #11242
[email protected] (#11242)
152 Структурные паттерны / Адаптер #11242
Адаптер классов
[email protected] (#11242)
153 Структурные паттерны / Адаптер #11242
Псевдокод
В этом шуточном примере Адаптер преобразует один интер-
фейс в другой, позволяя совместить квадратные колышки и
круглые отверстия.
[email protected] (#11242)
154 Структурные паттерны / Адаптер #11242
[email protected] (#11242)
155 Структурные паттерны / Адаптер #11242
Применимость
Когда вы хотите использовать сторонний класс, но его
интерфейс не соответствует остальному коду приложения.
[email protected] (#11242)
156 Структурные паттерны / Адаптер #11242
Шаги реализации
1. Убедитесь, что у вас есть два класса с несовместимыми
интерфейсами:
[email protected] (#11242)
157 Структурные паттерны / Адаптер #11242
Преимущества и недостатки
Отделяет и скрывает от клиента подробности преобразова-
ния различных интерфейсов.
[email protected] (#11242)
158 Структурные паттерны / Адаптер #11242
[email protected] (#11242)
159 Структурные паттерны / Мост #11242
МОСТ
Также известен как: Bridge
[email protected] (#11242)
160 Структурные паттерны / Мост #11242
Проблема
Абстракция? Реализация?! Звучит пугающе! Чтобы понять, о
чём идёт речь, давайте разберём очень простой пример.
[email protected] (#11242)
161 Структурные паттерны / Мост #11242
Решение
Корень проблемы заключается в том, что мы пытаемся рас-
ширить классы фигур сразу в двух независимых плоско-
стях — по виду и по цвету. Именно это приводит к
разрастанию дерева классов.
[email protected] (#11242)
162 Структурные паттерны / Мост #11242
Абстракция и Реализация
1
Эти термины были введены в книге GoF при описании
Моста. На мой взгляд, они выглядят слишком академичны-
ми, делая описание паттерна сложнее, чем он есть на самом
деле. Помня о примере с фигурами и цветами, давайте все
же разберёмся, что имели в виду авторы паттерна.
[email protected] (#11242)
163 Структурные паттерны / Мост #11242
[email protected] (#11242)
164 Структурные паттерны / Мост #11242
[email protected] (#11242)
165 Структурные паттерны / Мост #11242
Структура
[email protected] (#11242)
166 Структурные паттерны / Мост #11242
Псевдокод
В этом примере Мост разделяет монолитный код приборов
и пультов на две части: приборы (выступают реализацией) и
пульты управления ими (выступают абстракцией).
[email protected] (#11242)
167 Структурные паттерны / Мост #11242
[email protected] (#11242)
168 Структурные паттерны / Мост #11242
[email protected] (#11242)
169 Структурные паттерны / Мост #11242
33 method enable()
34 method disable()
35 method getVolume()
36 method setVolume(percent)
37 method getChannel()
38 method setChannel(channel)
39
40
41 // Но каждое устройство имеет особую реализацию.
42 class Tv implements Device is
43 // ...
44
45 class Radio implements Device is
46 // ...
47
48
49 // Где-то в клиентском коде.
50 tv = new Tv()
51 remote = new Remote(tv)
52 remote.togglePower()
53
54 radio = new Radio()
55 remote = new AdvancedRemote(radio)
Применимость
Когда вы хотите разделить монолитный класс, который
содержит несколько различных реализаций какой-то функ-
циональности (например, если класс может работать с раз-
ными системами баз данных).
[email protected] (#11242)
170 Структурные паттерны / Мост #11242
[email protected] (#11242)
171 Структурные паттерны / Мост #11242
Шаги реализации
1. Определите, существует ли в ваших классах два непересе-
кающихся измерения. Это может быть функциональность/
платформа, предметная-область/инфраструктура, фронт-
энд/бэк-энд или интерфейс/реализация.
[email protected] (#11242)
172 Структурные паттерны / Мост #11242
Преимущества и недостатки
Позволяет строить платформо-независимые программы.
[email protected] (#11242)
173 Структурные паттерны / Мост #11242
[email protected] (#11242)
174 Структурные паттерны / Компоновщик #11242
КОМПОНОВЩИК
Также известен как: Дерево, Composite
[email protected] (#11242)
175 Структурные паттерны / Компоновщик #11242
Проблема
Паттерн Компоновщик имеет смысл только тогда, когда
основная модель вашей программы может быть структури-
рована в виде дерева.
[email protected] (#11242)
176 Структурные паттерны / Компоновщик #11242
Решение
Компоновщик предлагает рассматривать Продукт и
Коробку через единый интерфейс с общим методом полу-
чения стоимости.
[email protected] (#11242)
177 Структурные паттерны / Компоновщик #11242
Аналогия из жизни
[email protected] (#11242)
178 Структурные паттерны / Компоновщик #11242
Структура
[email protected] (#11242)
179 Структурные паттерны / Компоновщик #11242
[email protected] (#11242)
180 Структурные паттерны / Компоновщик #11242
Псевдокод
В этом примере Компоновщик помогает реализовать вло-
женные геометрические фигуры.
[email protected] (#11242)
181 Структурные паттерны / Компоновщик #11242
[email protected] (#11242)
182 Структурные паттерны / Компоновщик #11242
23 method draw() is
24 // Нарисовать окружность в координате X, Y и радиусом R.
25
26 // Контейнер содержит операции добавления/удаления дочерних
27 // компонентов. Все стандартные операции интерфейса компонентов
28 // он делегирует каждому из дочерних компонентов.
29 class CompoundGraphic implements Graphic is
30 field children: array of Graphic
31
32 method add(child: Graphic) is
33 // Добавить компонент в список дочерних.
34
35 method remove(child: Graphic) is
36 // Убрать компонент из списка дочерних.
37
38 method move(x, y) is
39 foreach (child in children) do
40 child.move(x, y)
41
42 method draw() is
43 // 1. Для каждого дочернего компонента:
44 // - Отрисовать компонент.
45 // - Определить координаты максимальной границы.
46 // 2. Нарисовать пунктирную границу вокруг всей области.
47
48
49 // Приложение работает единообразно как с единичными
50 // компонентами, так и с целыми группами компонентов.
51 class ImageEditor is
52 field all: array of Graphic
53
54 method load() is
[email protected] (#11242)
183 Структурные паттерны / Компоновщик #11242
Применимость
Когда вам нужно представить древовидную структуру
объектов.
[email protected] (#11242)
184 Структурные паттерны / Компоновщик #11242
Шаги реализации
1. Убедитесь, что вашу бизнес-логику можно представить как
древовидную структуру. Попытайтесь разбить её на простые
компоненты и контейнеры. Помните, что контейнеры могут
содержать как простые компоненты, так и другие вложен-
ные контейнеры.
[email protected] (#11242)
185 Структурные паттерны / Компоновщик #11242
Преимущества и недостатки
Упрощает архитектуру клиента при работе со сложным
деревом компонентов.
Облегчает добавление новых видов компонентов.
[email protected] (#11242)
186 Структурные паттерны / Компоновщик #11242
[email protected] (#11242)
187 Структурные паттерны / Декоратор #11242
ДЕКОРАТОР
Также известен как: Wrapper, Обёртка, Decorator
[email protected] (#11242)
188 Структурные паттерны / Декоратор #11242
Проблема
Вы работаете над библиотекой оповещений, которую можно
подключать к разнообразным программам, чтобы получать
уведомления о важных событиях.
[email protected] (#11242)
189 Структурные паттерны / Декоратор #11242
[email protected] (#11242)
190 Структурные паттерны / Декоратор #11242
Решение
Наследование — это первое, что приходит в голову мно-
гим программистам, когда нужно расширить какое-то суще-
ствующее поведение. Но механизм наследования имеет
несколько досадных проблем.
[email protected] (#11242)
191 Структурные паттерны / Декоратор #11242
[email protected] (#11242)
192 Структурные паттерны / Декоратор #11242
[email protected] (#11242)
193 Структурные паттерны / Декоратор #11242
[email protected] (#11242)
194 Структурные паттерны / Декоратор #11242
Аналогия из жизни
[email protected] (#11242)
195 Структурные паттерны / Декоратор #11242
Структура
[email protected] (#11242)
196 Структурные паттерны / Декоратор #11242
Псевдокод
В этом примере Декоратор защищает финансовые данные
дополнительными уровнями безопасности прозрачно для
кода, который их использует.
[email protected] (#11242)
197 Структурные паттерны / Декоратор #11242
[email protected] (#11242)
198 Структурные паттерны / Декоратор #11242
[email protected] (#11242)
199 Структурные паттерны / Декоратор #11242
33 method writeData(data) is
34 // 1. Зашифровать поданные данные.
35 // 2. Передать зашифрованные данные в метод writeData
36 // обёрнутого объекта (wrappee).
37
38 method readData():data is
39 // 1. Получить данные из метода readData обёрнутого
40 // объекта (wrappee).
41 // 2. Расшифровать их, если они зашифрованы.
42 // 3. Вернуть результат.
43
44 // Декорировать можно не только базовые компоненты, но и уже
45 // обёрнутые объекты.
46 class CompressionDecorator extends DataSourceDecorator is
47 method writeData(data) is
48 // 1. Запаковать поданные данные.
49 // 2. Передать запакованные данные в метод writeData
50 // обёрнутого объекта (wrappee).
51
52 method readData():data is
53 // 1. Получить данные из метода readData обёрнутого
54 // объекта (wrappee).
55 // 2. Распаковать их, если они запакованы.
56 // 3. Вернуть результат.
57
58
59 // Вариант 1. Простой пример сборки и использования декораторов.
60 class Application is
61 method dumbUsageExample() is
62 source = new FileDataSource("somefile.dat")
63 source.writeData(salaryRecords)
64 // В файл были записаны чистые данные.
[email protected] (#11242)
200 Структурные паттерны / Декоратор #11242
[email protected] (#11242)
201 Структурные паттерны / Декоратор #11242
97 class ApplicationConfigurator is
98 method configurationExample() is
99 source = new FileDataSource("salary.dat")
100 if (enabledEncryption)
101 source = new EncryptionDecorator(source)
102 if (enabledCompression)
103 source = new CompressionDecorator(source)
104
105 logger = new SalaryManager(source)
106 salary = logger.load()
107 // ...
Применимость
Когда вам нужно добавлять обязанности объектам на лету,
незаметно для кода, который их использует.
[email protected] (#11242)
202 Структурные паттерны / Декоратор #11242
Шаги реализации
1. Убедитесь, что в вашей задаче есть один основной компо-
нент и несколько опциональных дополнений или надстроек
над ним.
[email protected] (#11242)
203 Структурные паттерны / Декоратор #11242
Преимущества и недостатки
Большая гибкость, чем у наследования.
[email protected] (#11242)
204 Структурные паттерны / Декоратор #11242
[email protected] (#11242)
205 Структурные паттерны / Декоратор #11242
[email protected] (#11242)
206 Структурные паттерны / Фасад #11242
ФАСАД
Также известен как: Facade
[email protected] (#11242)
207 Структурные паттерны / Фасад #11242
Проблема
Вашему коду приходится работать с большим количеством
объектов некой сложной библиотеки или фреймворка. Вы
должны самостоятельно инициализировать эти объекты,
следить за правильным порядком зависимостей и так далее.
Решение
Фасад — это простой интерфейс для работы со сложной
подсистемой, содержащей множество классов. Фасад может
иметь урезанный интерфейс, не имеющий 100% функцио-
нальности, которой можно достичь, используя сложную под-
систему напрямую. Но он предоставляет именно те фичи,
которые нужны клиенту, и скрывает все остальные.
[email protected] (#11242)
208 Структурные паттерны / Фасад #11242
Аналогия из жизни
Структура
[email protected] (#11242)
209 Структурные паттерны / Фасад #11242
Псевдокод
В этом примере Фасад упрощает работу со сложным фрейм-
ворком видеоконвертации.
[email protected] (#11242)
210 Структурные паттерны / Фасад #11242
[email protected] (#11242)
211 Структурные паттерны / Фасад #11242
10 class MPEG4CompressionCodec
11 // ...
12
13 class CodecFactory
14 // ...
15
16 class BitrateReader
17 // ...
18
19 class AudioMixer
20 // ...
21
22
23 // Вместо этого мы создаём Фасад — простой интерфейс для работы
24 // со сложным фреймворком. Фасад не имеет всей функциональности
25 // фреймворка, но зато скрывает его сложность от клиентов.
26 class VideoConverter is
27 method convert(filename, format):File is
28 file = new VideoFile(filename)
29 sourceCodec = new CodecFactory.extract(file)
30 if (format == "mp4")
31 destinationCodec = new MPEG4CompressionCodec()
32 else
33 destinationCodec = new OggCompressionCodec()
34 buffer = BitrateReader.read(filename, sourceCodec)
35 result = BitrateReader.convert(buffer, destinationCodec)
36 result = (new
new AudioMixer()).fix(result)
37 return new File(result)
38
39 // Приложение не зависит от сложного фреймворка конвертации
40 // видео. Кстати, если вы вдруг решите сменить фреймворк, вам
41 // нужно будет переписать только класс фасада.
[email protected] (#11242)
212 Структурные паттерны / Фасад #11242
42 class Application is
43 method main() is
44 convertor = new VideoConverter()
45 mp4 = convertor.convert("funny-cats-video.ogg", "mp4")
46 mp4.save()
Применимость
Когда вам нужно представить простой или урезанный
интерфейс к сложной подсистеме.
[email protected] (#11242)
213 Структурные паттерны / Фасад #11242
Шаги реализации
1. Определите, можно ли создать более простой интерфейс,
чем тот, который предоставляет сложная подсистема. Вы на
правильном пути, если этот интерфейс избавит клиента от
необходимости знать о подробностях подсистемы.
Преимущества и недостатки
Изолирует клиентов от компонентов сложной подсистемы.
[email protected] (#11242)
214 Структурные паттерны / Фасад #11242
[email protected] (#11242)
215 Структурные паттерны / Фасад #11242
[email protected] (#11242)
216 Структурные паттерны / Легковес #11242
ЛЕГКОВЕС
Также известен как: Приспособленец, Кэш, Flyweight
[email protected] (#11242)
217 Структурные паттерны / Легковес #11242
Проблема
На досуге вы решили написать небольшую игру, в которой
игроки перемещаются по карте и стреляют друг в друга.
Фишкой игры должна была стать реалистичная система
частиц. Пули, снаряды, осколки от взрывов — всё это долж-
но красиво летать и радовать взгляд.
[email protected] (#11242)
218 Структурные паттерны / Легковес #11242
Решение
Если внимательно посмотреть на класс частиц, то можно
заметить, что цвет и спрайт занимают больше всего памяти.
Более того, они хранятся в каждом объекте, хотя фактически
их значения одинаковы для большинства частиц.
[email protected] (#11242)
219 Структурные паттерны / Легковес #11242
[email protected] (#11242)
220 Структурные паттерны / Легковес #11242
[email protected] (#11242)
221 Структурные паттерны / Легковес #11242
Неизменяемость Легковесов
Фабрика Легковесов
[email protected] (#11242)
222 Структурные паттерны / Легковес #11242
Структура
[email protected] (#11242)
223 Структурные паттерны / Легковес #11242
Псевдокод
В этом примере Легковес помогает сэкономить оператив-
ную память при отрисовке на экране миллионов объектов-
деревьев.
[email protected] (#11242)
224 Структурные паттерны / Легковес #11242
[email protected] (#11242)
225 Структурные паттерны / Легковес #11242
[email protected] (#11242)
226 Структурные паттерны / Легковес #11242
33 class Tree is
34 field x,y
35 field type: TreeType
36 constructor Tree(x, y, type) { ... }
37 method draw(canvas) is
38 type.draw(canvas, this
this.x, this
this.y)
39
40 // Классы Tree и Forest являются клиентами Легковеса. При
41 // желании их можно слить в один класс, если вам не нужно
42 // расширять класс деревьев далее.
43 class Forest is
44 field trees: collection of Trees
45
46 method plantTree(x, y, name, color, texture) is
47 type = TreeFactory.getTreeType(name, color, texture)
48 tree = new Tree(x, y, type)
49 trees.add(tree)
50
51 method draw(canvas) is
52 foreach (tree in trees) do
53 tree.draw(canvas)
Применимость
Когда не хватает оперативной памяти для поддержки всех
нужных объектов.
[email protected] (#11242)
227 Структурные паттерны / Легковес #11242
Шаги реализации
1. Разделите поля класса, который станет легковесом, на
две части:
[email protected] (#11242)
228 Структурные паттерны / Легковес #11242
Преимущества и недостатки
Экономит оперативную память.
[email protected] (#11242)
229 Структурные паттерны / Легковес #11242
[email protected] (#11242)
230 Структурные паттерны / Заместитель #11242
ЗАМЕСТИТЕЛЬ
Также известен как: Proxy
[email protected] (#11242)
231 Структурные паттерны / Заместитель #11242
Проблема
Для чего вообще контролировать доступ к объектам? Рас-
смотрим такой пример: у вас есть внешний ресурсоёмкий
объект, который нужен не все время, а изредка.
Решение
Паттерн Заместитель предлагает создать новый класс-дуб-
лёр, имеющий тот же интерфейс, что и оригинальный слу-
жебный объект. При получении запроса от клиента
[email protected] (#11242)
232 Структурные паттерны / Заместитель #11242
Аналогия из жизни
[email protected] (#11242)
233 Структурные паттерны / Заместитель #11242
Структура
[email protected] (#11242)
234 Структурные паттерны / Заместитель #11242
Псевдокод
В этом примере Заместитель помогает добавить в програм-
му механизм ленивой инициализации и кеширования
результатов работы библиотеки интеграции с YouTube.
[email protected] (#11242)
235 Структурные паттерны / Заместитель #11242
[email protected] (#11242)
236 Структурные паттерны / Заместитель #11242
[email protected] (#11242)
237 Структурные паттерны / Заместитель #11242
41
42 method getVideoInfo(id) is
43 if (videoCache == null || needReset)
44 videoCache = service.getVideoInfo(id)
45 return videoCache
46
47 method downloadVideo(id) is
48 if (!downloadExists(id) || needReset)
49 service.downloadVideo(id)
50
51 // Класс GUI, который использует сервисный объект. Вместо
52 // реального сервиса, мы подсунем ему объект-заместитель. Клиент
53 // ничего не заметит, так как заместитель имеет тот же
54 // интерфейс, что и сервис.
55 class YouTubeManager is
56 protected field service: ThirdPartyYouTubeLib
57
58 constructor YouTubeManager(service: ThirdPartyYouTubeLib) is
59 this
this.service = service
60
61 method renderVideoPage(id) is
62 info = service.getVideoInfo(id)
63 // Отобразить страницу видеоролика.
64
65 method renderListPanel() is
66 list = service.listVideos()
67 // Отобразить список превьюшек видеороликов.
68
69 method reactOnUserInput() is
70 renderVideoPage()
71 renderListPanel()
72
[email protected] (#11242)
238 Структурные паттерны / Заместитель #11242
Применимость
Ленивая инициализация (виртуальный прокси). Когда у вас
есть тяжёлый объект, грузящий данные из файловой систе-
мы или базы данных.
[email protected] (#11242)
239 Структурные паттерны / Заместитель #11242
[email protected] (#11242)
240 Структурные паттерны / Заместитель #11242
Шаги реализации
1. Определите интерфейс, который бы сделал заместитель и
оригинальный объект взаимозаменяемыми.
Преимущества и недостатки
Позволяет контролировать сервисный объект незаметно для
клиента.
[email protected] (#11242)
241 Структурные паттерны / Заместитель #11242
[email protected] (#11242)
242 Поведенческие паттерны #11242
Поведенческие паттерны
Эти паттерны решают задачи эффективного и безопасного
взаимодействия между объектами программы.
Цепочка обязанностей
Chain of Responsibility
Команда
Command
[email protected] (#11242)
243 Поведенческие паттерны #11242
Итератор
Iterator
Посредник
Mediator
Снимок
Memento
[email protected] (#11242)
244 Поведенческие паттерны #11242
Наблюдатель
Observer
Состояние
State
Стратегия
Strategy
[email protected] (#11242)
245 Поведенческие паттерны #11242
Шаблонный метод
Template Method
Посетитель
Visitor
[email protected] (#11242)
246 Поведенческие паттерны / Цепочка обязанностей #11242
ЦЕПОЧКА
ОБЯЗАННОСТЕЙ
Также известен как: CoR, Chain of Command, Chain of Responsibility
[email protected] (#11242)
247 Поведенческие паттерны / Цепочка обязанностей #11242
Проблема
Представьте, что вы делаете систему приёма онлайн-зака-
зов. Вы хотите ограничить к ней доступ так, чтобы толь-
ко авторизованные пользователи могли создавать заказы.
Кроме того, определённые пользователи, владеющие права-
ми администратора, должны иметь полный доступ к заказам.
[email protected] (#11242)
248 Поведенческие паттерны / Цепочка обязанностей #11242
[email protected] (#11242)
249 Поведенческие паттерны / Цепочка обязанностей #11242
Решение
Как и многие другие поведенческие паттерны, Цепочка обя-
занностей базируется на том, чтобы превратить отдельные
поведения в объекты. В нашем случае каждая проверка
переедет в отдельный класс с единственным методом
выполнения. Данные запроса, над которым происходит про-
верка, будут передаваться в метод как аргументы.
[email protected] (#11242)
250 Поведенческие паттерны / Цепочка обязанностей #11242
[email protected] (#11242)
251 Поведенческие паттерны / Цепочка обязанностей #11242
Аналогия из жизни
Вы купили новую видеокарту. Она автоматически опреде-
лилась и заработала под Windows, но в вашей любимой
Ubuntu «завести» её не удалось. Со слабой надеждой вы
звоните в службу поддержки.
[email protected] (#11242)
252 Поведенческие паттерны / Цепочка обязанностей #11242
[email protected] (#11242)
253 Поведенческие паттерны / Цепочка обязанностей #11242
Структура
[email protected] (#11242)
254 Поведенческие паттерны / Цепочка обязанностей #11242
[email protected] (#11242)
255 Поведенческие паттерны / Цепочка обязанностей #11242
Псевдокод
В этом примере Цепочка обязанностей отвечает за показ
контекстной помощи для активных элементов пользователь-
ского интерфейса.
[email protected] (#11242)
256 Поведенческие паттерны / Цепочка обязанностей #11242
[email protected] (#11242)
257 Поведенческие паттерны / Цепочка обязанностей #11242
1 // Интерфейс обработчиков.
2 interface ComponentWithContextualHelp is
3 method showHelp()
4
5
6 // Базовый класс простых компонентов.
7 abstract class Component implements ComponentWithContextualHelp is
8 field tooltipText: string
9
10 // Контейнер, содержащий компонент, служит в качестве
11 // следующего звена цепочки.
12 protected field container: Container
13
14 // Базовое поведение компонента заключается в том, чтобы
15 // показать всплывающую подсказку, если для неё задан текст.
16 // В обратном случае — перенаправить запрос своему
17 // контейнеру, если тот существует.
18 method showHelp() is
19 if (tooltipText != null
null)
20 // Показать подсказку.
21 else
22 container.showHelp()
23
24
25 // Контейнеры могут включать в себя как простые компоненты, так
26 // и другие контейнеры. Здесь формируются связи цепочки. Класс
27 // контейнера унаследует метод showHelp от своего родителя —
[email protected] (#11242)
258 Поведенческие паттерны / Цепочка обязанностей #11242
28 // базового компонента.
29 abstract class Container extends Component is
30 protected field children: array of Component
31
32 method add(child) is
33 children.add(child)
34 child.container = this
35
36
37 // Большинство примитивных компонентов устроит базовое поведение
38 // показа помощи через подсказку, которое они унаследуют из
39 // класса Component.
40 class Button extends Component is
41 // ...
42
43 // Но сложные компоненты могут переопределять метод показа
44 // помощи по-своему. Но и в этом случае они всегда могут
45 // вернуться к базовой реализации, вызвав метод родителя.
46 class Panel extends Container is
47 field modalHelpText: string
48
49 method showHelp() is
50 if (modalHelpText != null
null)
51 // Показать модальное окно с помощью.
52 else
53 super
super.showHelp()
54
55 // ...то же, что и выше...
56 class Dialog extends Container is
57 field wikiPageURL: string
58
59 method showHelp() is
[email protected] (#11242)
259 Поведенческие паттерны / Цепочка обязанностей #11242
60 if (wikiPageURL != null
null)
61 // Открыть страницу Wiki в браузере.
62 else
63 super
super.showHelp()
64
65
66 // Клиентский код.
67 class Application is
68 // Каждое приложение конфигурирует цепочку по-своему.
69 method createUI() is
70 dialog = new Dialog("Budget Reports")
71 dialog.wikiPageURL = "http://..."
72 panel = new Panel(0, 0, 400, 800)
73 panel.modalHelpText = "This panel does..."
74 ok = new Button(250, 760, 50, 20, "OK")
75 ok.tooltipText = "This is an OK button that..."
76 cancel = new Button(320, 760, 50, 20, "Cancel")
77 // ...
78 panel.add(ok)
79 panel.add(cancel)
80 dialog.add(panel)
81
82 // Представьте, что здесь произойдёт.
83 method onF1KeyPress() is
84 component = this
this.getComponentAtMouseCoords()
85 component.showHelp()
Применимость
Когда программа должна обрабатывать разнообразные
запросы несколькими способами, но заранее неизвестно,
[email protected] (#11242)
260 Поведенческие паттерны / Цепочка обязанностей #11242
Шаги реализации
1. Создайте интерфейс обработчика и опишите в нём основной
метод обработки.
[email protected] (#11242)
261 Поведенческие паттерны / Цепочка обязанностей #11242
[email protected] (#11242)
262 Поведенческие паттерны / Цепочка обязанностей #11242
Преимущества и недостатки
Уменьшает зависимость между клиентом и обработчиками.
[email protected] (#11242)
263 Поведенческие паттерны / Цепочка обязанностей #11242
[email protected] (#11242)
264 Поведенческие паттерны / Цепочка обязанностей #11242
[email protected] (#11242)
265 Поведенческие паттерны / Команда #11242
КОМАНДА
Также известен как: Действие, Транзакция, Action, Command
[email protected] (#11242)
266 Поведенческие паттерны / Команда #11242
Проблема
Представьте, что вы работаете над программой текстово-
го редактора. Дело как раз подошло к разработке панели
управления. Вы создали класс красивых Кнопок и хоти-
те использовать его для всех кнопок приложения, начиная
от панели управления, заканчивая простыми кнопками в
диалогах.
[email protected] (#11242)
267 Поведенческие паттерны / Команда #11242
[email protected] (#11242)
268 Поведенческие паттерны / Команда #11242
Решение
Хорошие программы обычно структурированы в виде слоёв.
Самый распространённый пример — слои пользовательско-
го интерфейса и бизнес-логики. Первый всего лишь рисует
красивую картинку для пользователя. Но когда нужно сде-
лать что-то важное, интерфейс «просит» слой бизнес-логики
заняться этим.
[email protected] (#11242)
269 Поведенческие паттерны / Команда #11242
[email protected] (#11242)
270 Поведенческие паттерны / Команда #11242
[email protected] (#11242)
271 Поведенческие паттерны / Команда #11242
Аналогия из жизни
[email protected] (#11242)
272 Поведенческие паттерны / Команда #11242
Структура
[email protected] (#11242)
273 Поведенческие паттерны / Команда #11242
Псевдокод
В этом примере паттерн Команда служит для ведения исто-
рии выполненных операций, позволяя отменять их, если
потребуется.
[email protected] (#11242)
274 Поведенческие паттерны / Команда #11242
[email protected] (#11242)
275 Поведенческие паттерны / Команда #11242
[email protected] (#11242)
276 Поведенческие паттерны / Команда #11242
29 // Конкретные команды.
30 class CopyCommand extends Command is
31 // Команда копирования не записывается в историю, так как
32 // она не меняет состояние редактора.
33 method execute() is
34 app.clipboard = editor.getSelection()
35 return false
36
37 class CutCommand extends Command is
38 // Команды, меняющие состояние редактора, сохраняют
39 // состояние редактора перед своим действием и сигнализируют
40 // об изменении, возвращая true.
41 method execute() is
42 saveBackup()
43 app.clipboard = editor.getSelection()
44 editor.deleteSelection()
45 return true
46
47 class PasteCommand extends Command is
48 method execute() is
49 saveBackup()
50 editor.replaceSelection(app.clipboard)
51 return true
52
53 // Отмена — это тоже команда.
54 class UndoCommand extends Command is
55 method execute() is
56 app.undo()
57 return false
58
59
60 // Глобальная история команд — это стек.
[email protected] (#11242)
277 Поведенческие паттерны / Команда #11242
61 class CommandHistory is
62 private field history: array of Command
63
64 // Последний зашедший...
65 method push(c: Command) is
66 // Добавить команду в конец массива-истории.
67
68 // ...выходит первым.
69 method pop():Command is
70 // Достать последнюю команду из массива-истории.
71
72
73 // Класс редактора содержит непосредственные операции над
74 // текстом. Он отыгрывает роль получателя — команды делегируют
75 // ему свои действия.
76 class Editor is
77 field text: string
78
79 method getSelection() is
80 // Вернуть выбранный текст.
81
82 method deleteSelection() is
83 // Удалить выбранный текст.
84
85 method replaceSelection(text) is
86 // Вставить текст из буфера обмена в текущей позиции.
87
88
89 // Класс приложения настраивает объекты для совместной работы.
90 // Он выступает в роли отправителя — создаёт команды, чтобы
91 // выполнить какие-то действия.
92 class Application is
[email protected] (#11242)
278 Поведенческие паттерны / Команда #11242
[email protected] (#11242)
279 Поведенческие паттерны / Команда #11242
125 if (command.execute())
126 history.push(command)
127
128 // Берём последнюю команду из истории и заставляем её все
129 // отменить. Мы не знаем конкретный тип команды, но это и не
130 // важно, так как каждая команда знает, как отменить своё
131 // действие.
132 method undo() is
133 command = history.pop()
134 if (command != null
null)
135 command.undo()
Применимость
Когда вы хотите параметризовать объекты выполняемым
действием.
[email protected] (#11242)
280 Поведенческие паттерны / Команда #11242
[email protected] (#11242)
281 Поведенческие паттерны / Команда #11242
Шаги реализации
1. Создайте общий интерфейс команд и определите в нём
метод запуска.
[email protected] (#11242)
282 Поведенческие паттерны / Команда #11242
Преимущества и недостатки
Убирает прямую зависимость между объектами, вызываю-
щими операции, и объектами, которые их непосредственно
выполняют.
Позволяет реализовать простую отмену и повтор операций.
[email protected] (#11242)
283 Поведенческие паттерны / Команда #11242
[email protected] (#11242)
284 Поведенческие паттерны / Команда #11242
[email protected] (#11242)
285 Поведенческие паттерны / Итератор #11242
ИТЕРАТОР
Также известен как: Iterator
[email protected] (#11242)
286 Поведенческие паттерны / Итератор #11242
Проблема
Коллекции — самая распространённая структура данных,
которую вы можете встретить в программировании. Это
набор объектов, собранный в одну кучу по каким-то
критериям.
[email protected] (#11242)
287 Поведенческие паттерны / Итератор #11242
Решение
Идея паттерна Итератор состоит в том, чтобы вынести пове-
дение обхода коллекции из самой коллекции в отдель-
ный класс.
[email protected] (#11242)
288 Поведенческие паттерны / Итератор #11242
[email protected] (#11242)
289 Поведенческие паттерны / Итератор #11242
Аналогия из жизни
[email protected] (#11242)
290 Поведенческие паттерны / Итератор #11242
Структура
[email protected] (#11242)
291 Поведенческие паттерны / Итератор #11242
Псевдокод
В этом примере паттерн Итератор используется для реа-
лизации обхода нестандартной коллекции, которая инкап-
сулирует доступ к социальному графу Facebook. Коллекция
предоставляет несколько итераторов, которые могут по-раз-
ному обходить профили людей.
[email protected] (#11242)
292 Поведенческие паттерны / Итератор #11242
[email protected] (#11242)
293 Поведенческие паттерны / Итератор #11242
[email protected] (#11242)
294 Поведенческие паттерны / Итератор #11242
26 // Конкретный итератор.
27 class FacebookIterator implements ProfileIterator is
28 // Итератору нужна ссылка на коллекцию, которую он обходит.
29 private field facebook: Facebook
30 private field profileId, type: string
31
32 // Но каждый итератор обходит коллекцию, независимо от
33 // остальных, поэтому он содержит информацию о текущей
34 // позиции обхода.
35 private field currentPosition
36 private field cache: array of Profile
37
38 constructor FacebookIterator(facebook, profileId, type) is
39 this
this.facebook = facebook
40 this
this.profileId = profileId
41 this
this.type = type
42
43 private method lazyInit() is
44 if (cache == null
null)
45 cache = facebook.socialGraphRequest(profileId, type)
46
47 // Итератор реализует методы базового интерфейса по-своему.
48 method getNext() is
49 if (hasMore())
50 currentPosition++
51 return cache[currentPosition]
52
53 method hasMore() is
54 lazyInit()
55 return currentPosition < cache.length
56
57
[email protected] (#11242)
295 Поведенческие паттерны / Итератор #11242
[email protected] (#11242)
296 Поведенческие паттерны / Итератор #11242
Применимость
Когда у вас есть сложная структура данных, и вы хотите
скрыть от клиента детали её реализации (из-за сложности
или вопросов безопасности).
[email protected] (#11242)
297 Поведенческие паттерны / Итератор #11242
Шаги реализации
1. Создайте общий интерфейс итераторов. Обязательный
минимум — это операция получения следующего элемента
коллекции. Но для удобства можно предусмотреть и другое.
Например, методы для получения предыдущего элемента,
текущей позиции, проверки окончания обхода и прочие.
[email protected] (#11242)
298 Поведенческие паттерны / Итератор #11242
Преимущества и недостатки
Упрощает классы хранения данных.
[email protected] (#11242)
299 Поведенческие паттерны / Посредник #11242
ПОСРЕДНИК
Также известен как: Intermediary, Controller, Mediator
[email protected] (#11242)
300 Поведенческие паттерны / Посредник #11242
Проблема
Предположим, что у вас есть диалог создания профиля поль-
зователя. Он состоит из всевозможных элементов управле-
ния — текстовых полей, чекбоксов, кнопок.
[email protected] (#11242)
301 Поведенческие паттерны / Посредник #11242
Решение
Паттерн Посредник заставляет объекты общаться не напря-
мую друг с другом, а через отдельный объект-посредник,
который знает, кому нужно перенаправить тот или иной
запрос. Благодаря этому, компоненты системы будут зави-
сеть только от посредника, а не от десятков других
компонентов.
[email protected] (#11242)
302 Поведенческие паттерны / Посредник #11242
[email protected] (#11242)
303 Поведенческие паттерны / Посредник #11242
Аналогия из жизни
[email protected] (#11242)
304 Поведенческие паттерны / Посредник #11242
Структура
[email protected] (#11242)
305 Поведенческие паттерны / Посредник #11242
Псевдокод
В этом примере Посредник помогает избавиться от зави-
симостей между классами различных элементов пользова-
тельского интерфейса: кнопками, чекбоксами и надписями.
[email protected] (#11242)
306 Поведенческие паттерны / Посредник #11242
[email protected] (#11242)
307 Поведенческие паттерны / Посредник #11242
[email protected] (#11242)
308 Поведенческие паттерны / Посредник #11242
40 // формы логина.
41 if (!found)
42 // Показать ошибку над формой логина.
43 else
44 // 1. Создать пользовательский аккаунт с данными
45 // из формы регистрации.
46 // 2. Авторизировать этого пользователя.
47 // ...
48
49 // Классы компонентов общаются с посредниками через их общий
50 // интерфейс. Благодаря этому одни и те же компоненты можно
51 // использовать в разных посредниках.
52 class Component is
53 field dialog: Mediator
54
55 constructor Component(dialog) is
56 this
this.dialog = dialog
57
58 method click() is
59 dialog.notify(this
this, "click")
60
61 method keypress() is
62 dialog.notify(this
this, "keypress")
63
64 // Конкретные компоненты не связаны между собой напрямую. У них
65 // есть только один канал общения — через отправку уведомлений
66 // посреднику.
67 class Button extends Component is
68 // ...
69
70 class Textbox extends Component is
71 // ...
[email protected] (#11242)
309 Поведенческие паттерны / Посредник #11242
Применимость
Когда вам сложно менять некоторые классы из-за того, что
они имеют множество хаотичных связей с другими
классами.
[email protected] (#11242)
310 Поведенческие паттерны / Посредник #11242
Шаги реализации
1. Найдите группу тесно переплетённых классов, отвязав кото-
рые друг от друга, можно получить некоторую пользу.
Например, чтобы повторно использовать их код в другой
программе.
[email protected] (#11242)
311 Поведенческие паттерны / Посредник #11242
Преимущества и недостатки
Устраняет зависимости между компонентами, позволяя
повторно их использовать.
Упрощает взаимодействие между компонентами.
[email protected] (#11242)
312 Поведенческие паттерны / Посредник #11242
[email protected] (#11242)
313 Поведенческие паттерны / Посредник #11242
[email protected] (#11242)
314 Поведенческие паттерны / Снимок #11242
СНИМОК
Также известен как: Хранитель, Memento
[email protected] (#11242)
315 Поведенческие паттерны / Снимок #11242
Проблема
Предположим, что вы пишете программу текстового редак-
тора. Помимо обычного редактирования, ваш редактор поз-
воляет менять форматирование текста, вставлять картинки и
прочее.
[email protected] (#11242)
316 Поведенческие паттерны / Снимок #11242
[email protected] (#11242)
317 Поведенческие паттерны / Снимок #11242
Решение
Все проблемы, описанные выше, возникают из-за наруше-
ния инкапсуляции. Это когда одни объекты пытаются сделать
работу за других, влезая в их приватную зону, чтобы собрать
необходимые для операции данные.
[email protected] (#11242)
318 Поведенческие паттерны / Снимок #11242
[email protected] (#11242)
319 Поведенческие паттерны / Снимок #11242
Структура
Классическая реализация на вложенных классах
[email protected] (#11242)
320 Поведенческие паттерны / Снимок #11242
[email protected] (#11242)
321 Поведенческие паттерны / Снимок #11242
[email protected] (#11242)
322 Поведенческие паттерны / Снимок #11242
[email protected] (#11242)
323 Поведенческие паттерны / Снимок #11242
Псевдокод
В этом примере паттерн Снимок используется совместно с
паттерном Команда и позволяет хранить резервные копии
сложного состояния текстового редактора и восстанавливать
его, если потребуется.
[email protected] (#11242)
324 Поведенческие паттерны / Снимок #11242
[email protected] (#11242)
325 Поведенческие паттерны / Снимок #11242
Применимость
Когда вам нужно сохранять мгновенные снимки состояния
объекта (или его части), чтобы впоследствии объект можно
было восстановить в том же состоянии.
[email protected] (#11242)
326 Поведенческие паттерны / Снимок #11242
Шаги реализации
1. Определите класс создателя, объекты которого должны
создавать снимки своего состояния.
[email protected] (#11242)
327 Поведенческие паттерны / Снимок #11242
Преимущества и недостатки
Не нарушает инкапсуляции исходного объекта.
[email protected] (#11242)
328 Поведенческие паттерны / Снимок #11242
[email protected] (#11242)
329 Поведенческие паттерны / Наблюдатель #11242
НАБЛЮДАТЕЛЬ
Также известен как: Издатель-Подписчик, Слушатель, Observer
[email protected] (#11242)
330 Поведенческие паттерны / Наблюдатель #11242
Проблема
Представьте, что вы имеете два объекта: Покупатель и
Магазин . В магазин вот-вот должны завезти новый товар,
который интересен покупателю.
[email protected] (#11242)
331 Поведенческие паттерны / Наблюдатель #11242
Решение
Давайте называть Издателями те объекты, которые содер-
жат важное или интересное для других состояние. Осталь-
ные объекты, которые хотят отслеживать изменения этого
состояния, назовём Подписчиками .
Подписка на события.
[email protected] (#11242)
332 Поведенческие паттерны / Наблюдатель #11242
Оповещения о событиях.
Аналогия из жизни
После того как вы оформили подписку на газету или журнал,
вам больше не нужно ездить в супермаркет и проверять,
[email protected] (#11242)
333 Поведенческие паттерны / Наблюдатель #11242
Структура
[email protected] (#11242)
334 Поведенческие паттерны / Наблюдатель #11242
[email protected] (#11242)
335 Поведенческие паттерны / Наблюдатель #11242
Псевдокод
В этом примере Наблюдатель позволяет объекту текстового
редактора оповещать другие объекты об изменениях своего
состояния.
[email protected] (#11242)
336 Поведенческие паттерны / Наблюдатель #11242
[email protected] (#11242)
337 Поведенческие паттерны / Наблюдатель #11242
21 class Editor is
22 public field events: EventManager
23 private field file: File
24
25 constructor Editor() is
26 events = new EventManager()
27
28 // Методы бизнес-логики, которые оповещают подписчиков об
29 // изменениях.
30 method openFile(path) is
31 this
this.file = new File(path)
32 events.notify("open", file.name)
33
34 method saveFile() is
35 file.write()
36 events.notify("save", file.name)
37 // ...
38
39
40 // Общий интерфейс подписчиков. Во многих языках, поддерживающих
41 // функциональные типы, можно обойтись без этого интерфейса и
42 // конкретных классов, заменив объекты подписчиков функциями.
43 interface EventListener is
44 method update(filename)
45
46 // Набор конкретных подписчиков. Они реализуют добавочную
47 // функциональность, реагируя на извещения от издателя.
48 class LoggingListener implements EventListener is
49 private field log: File
50 private field message
51
52 constructor LoggingListener(log_filename, message) is
[email protected] (#11242)
338 Поведенческие паттерны / Наблюдатель #11242
53 this
this.log = new File(log_filename)
54 this
this.message = message
55
56 method update(filename) is
57 log.write(replace('%s',filename,message))
58
59 class EmailAlertsListener implements EventListener is
60 private field email: string
61
62 constructor EmailAlertsListener(email, message) is
63 this
this.email = email
64 this
this.message = message
65
66 method update(filename) is
67 system.email(email, replace('%s',filename,message))
68
69
70 // Приложение может сконфигурировать издателей и подписчиков как
71 // угодно, в зависимости от целей и окружения.
72 class Application is
73 method config() is
74 editor = new TextEditor()
75
76 logger = new LoggingListener(
77 "/path/to/log.txt",
78 "Someone has opened file: %s");
79 editor.events.subscribe("open", logger)
80
81 emailAlerts = new EmailAlertsListener(
82 "[email protected]",
83 "Someone has changed the file: %s")
84 editor.events.subscribe("save", emailAlerts)
[email protected] (#11242)
339 Поведенческие паттерны / Наблюдатель #11242
Применимость
Когда после изменения состояния одного объекта требует-
ся что-то сделать в других, но вы не знаете наперёд, какие
именно объекты должны отреагировать.
Шаги реализации
1. Разбейте вашу функциональность на две части: независимое
ядро и опциональные зависимые части. Независимое ядро
станет издателем. Зависимые части станут подписчиками.
[email protected] (#11242)
340 Поведенческие паттерны / Наблюдатель #11242
[email protected] (#11242)
341 Поведенческие паттерны / Наблюдатель #11242
Преимущества и недостатки
Издатели не зависят от конкретных классов подписчиков и
наоборот.
Вы можете подписывать и отписывать получателей на лету.
[email protected] (#11242)
342 Поведенческие паттерны / Наблюдатель #11242
[email protected] (#11242)
343 Поведенческие паттерны / Наблюдатель #11242
[email protected] (#11242)
344 Поведенческие паттерны / Состояние #11242
СОСТОЯНИЕ
Также известен как: State
[email protected] (#11242)
345 Поведенческие паттерны / Состояние #11242
Проблема
Паттерн Состояние невозможно рассматривать в отрыве от
концепции машины состояний, также известной как стейт-
машина или конечный автомат.
Конечный автомат.
[email protected] (#11242)
346 Поведенческие паттерны / Состояние #11242
[email protected] (#11242)
347 Поведенческие паттерны / Состояние #11242
1 class Document is
2 field state: string
3 // ...
4 method publish() is
5 switch (state)
6 "draft":
7 state = "moderation"
8 break
9 "moderation":
10 if (currentUser.role == 'admin')
11 state = "published"
12 break
13 "published":
14 // Do nothing.
15 break
16 // ...
[email protected] (#11242)
348 Поведенческие паттерны / Состояние #11242
Решение
Паттерн Состояние предлагает создать отдельные классы
для каждого состояния, в котором может пребывать объ-
ект, а затем вынести туда поведения, соответствующие этим
состояниям.
[email protected] (#11242)
349 Поведенческие паттерны / Состояние #11242
Аналогия из жизни
Ваш смартфон ведёт себя по-разному, в зависимости от
текущего состояния:
[email protected] (#11242)
350 Поведенческие паттерны / Состояние #11242
Структура
[email protected] (#11242)
351 Поведенческие паттерны / Состояние #11242
Псевдокод
[email protected] (#11242)
352 Поведенческие паттерны / Состояние #11242
[email protected] (#11242)
353 Поведенческие паттерны / Состояние #11242
24 if (player.playing)
25 player.changeState(new
new PlayingState(player))
26 else
27 player.changeState(new
new ReadyState(player))
28
29 method clickPlay() is
30 // Ничего не делать.
31
32 method clickNext() is
33 // Ничего не делать.
34
35 method clickPrevious() is
36 // Ничего не делать.
37
38
39 // Конкретные состояния сами могут переводить контекст в другое
40 // состояние.
41 class ReadyState extends State is
42 method clickLock() is
43 player.changeState(new
new LockedState(player))
44
45 method clickPlay() is
46 player.startPlayback()
47 player.changeState(new
new PlayingState(player))
48
49 method clickNext() is
50 player.nextSong()
51
52 method clickPrevious() is
53 player.previousSong()
54
55
[email protected] (#11242)
354 Поведенческие паттерны / Состояние #11242
[email protected] (#11242)
355 Поведенческие паттерны / Состояние #11242
88 UI = new UserInterface()
89 UI.lockButton.onClick(this.clickLock)
90 UI.playButton.onClick(this.clickPlay)
91 UI.nextButton.onClick(this.clickNext)
92 UI.prevButton.onClick(this.clickPrevious)
93
94 // Другие объекты тоже должны иметь возможность заменять
95 // состояние проигрывателя.
96 method changeState(state: State) is
97 this
this.state = state
98
99 // Методы UI будут делегировать работу активному состоянию.
100 method clickLock() is
101 state.clickLock()
102 method clickPlay() is
103 state.clickPlay()
104 method clickNext() is
105 state.clickNext()
106 method clickPrevious() is
107 state.clickPrevious()
108
109 // Сервисные методы контекста, вызываемые состояниями.
110 method startPlayback() is
111 // ...
112 method stopPlayback() is
113 // ...
114 method nextSong() is
115 // ...
116 method previousSong() is
117 // ...
118 method fastForward(time) is
119 // ...
[email protected] (#11242)
356 Поведенческие паттерны / Состояние #11242
Применимость
Когда у вас есть объект, поведение которого кардинально
меняется в зависимости от внутреннего состояния, причём
типов состояний много, и их код часто меняется.
[email protected] (#11242)
357 Поведенческие паттерны / Состояние #11242
Шаги реализации
1. Определитесь с классом, который будет играть роль контек-
ста. Это может быть как существующий класс, в котором уже
есть зависимость от состояния, так и новый класс, если код
состояний размазан по нескольким классам.
[email protected] (#11242)
358 Поведенческие паттерны / Состояние #11242
Преимущества и недостатки
Избавляет от множества больших условных операторов
машины состояний.
Концентрирует в одном месте код, связанный с определён-
ным состоянием.
Упрощает код контекста.
[email protected] (#11242)
359 Поведенческие паттерны / Состояние #11242
[email protected] (#11242)
360 Поведенческие паттерны / Стратегия #11242
СТРАТЕГИЯ
Также известен как: Strategy
[email protected] (#11242)
361 Поведенческие паттерны / Стратегия #11242
Проблема
Вы решили написать приложение-навигатор для путеше-
ственников. Оно должно показывать красивую и удобную
карту, позволяющую с лёгкостью ориентироваться в незна-
комом городе.
[email protected] (#11242)
362 Поведенческие паттерны / Стратегия #11242
Решение
Паттерн Стратегия предлагает определить семейство схожих
алгоритмов, которые часто изменяются или расширяются, и
[email protected] (#11242)
363 Поведенческие паттерны / Стратегия #11242
[email protected] (#11242)
364 Поведенческие паттерны / Стратегия #11242
Аналогия из жизни
[email protected] (#11242)
365 Поведенческие паттерны / Стратегия #11242
Структура
[email protected] (#11242)
366 Поведенческие паттерны / Стратегия #11242
Псевдокод
В этом примере контекст использует Стратегию для выпол-
нения той или иной арифметической операции.
[email protected] (#11242)
367 Поведенческие паттерны / Стратегия #11242
9 return a + b
10
11 class ConcreteStrategySubtract implements Strategy is
12 method execute(a, b) is
13 return a - b
14
15 class ConcreteStrategyMultiply implements Strategy is
16 method execute(a, b) is
17 return a * b
18
19 // Контекст всегда работает со стратегиями через общий
20 // интерфейс. Он не знает, какая именно стратегия ему подана.
21 class Context is
22 private strategy: Strategy
23
24 method setStrategy(Strategy strategy) is
25 this
this.strategy = strategy
26
27 method executeStrategy(int a, int b) is
28 return strategy.execute(a, b)
29
30 // Конкретная стратегия выбирается на более высоком уровне,
31 // например, конфигуратором всего приложения. Готовый объект-
32 // стратегия подаётся в клиентский объект, а затем может быть
33 // заменён другой стратегией в любой момент на лету.
34 class ExampleApplication is
35 method main() is
36 // 1. Создать объект контекста.
37 // 2. Получить первое число (n1).
38 // 3. Получить второе число (n2).
39 // 4. Получить желаемую операцию.
40 // 5. Затем, выбрать стратегию:
[email protected] (#11242)
368 Поведенческие паттерны / Стратегия #11242
Применимость
Когда вам нужно использовать разные вариации какого-то
алгоритма внутри одного объекта.
[email protected] (#11242)
369 Поведенческие паттерны / Стратегия #11242
Шаги реализации
1. Определите алгоритм, который подвержен частым измене-
ниям. Также подойдёт алгоритм, имеющий несколько вариа-
ций, которые выбираются во время выполнения программы.
[email protected] (#11242)
370 Поведенческие паттерны / Стратегия #11242
Преимущества и недостатки
Горячая замена алгоритмов на лету.
[email protected] (#11242)
371 Поведенческие паттерны / Стратегия #11242
[email protected] (#11242)
372 Поведенческие паттерны / Шаблонный метод #11242
ШАБЛОННЫЙ
МЕТОД
Также известен как: Template Method
[email protected] (#11242)
373 Поведенческие паттерны / Шаблонный метод #11242
Проблема
Вы пишете программу для дата-майнинга в офисных доку-
ментах. Пользователи будут загружать в неё документы в
разных форматах (PDF, DOC, CSV), а программа должна
извлекать из них полезную информацию.
[email protected] (#11242)
374 Поведенческие паттерны / Шаблонный метод #11242
Решение
Паттерн Шаблонный метод предлагает разбить алгоритм на
последовательность шагов, описать эти шаги в отдельных
методах и вызывать их в одном шаблонном методе друг за
другом.
[email protected] (#11242)
375 Поведенческие паттерны / Шаблонный метод #11242
[email protected] (#11242)
376 Поведенческие паттерны / Шаблонный метод #11242
Аналогия из жизни
[email protected] (#11242)
377 Поведенческие паттерны / Шаблонный метод #11242
Структура
[email protected] (#11242)
378 Поведенческие паттерны / Шаблонный метод #11242
Псевдокод
В этом примере Шаблонный метод используется как заго-
товка для стандартного искусственного интеллекта в про-
стой игре-стратегии. Для введения в игру новой расы
достаточно создать подкласс и реализовать в нём недоста-
ющие методы.
[email protected] (#11242)
379 Поведенческие паттерны / Шаблонный метод #11242
1 class GameAI is
2 // Шаблонный метод должен быть задан в базовом классе. Он
3 // состоит из вызовов методов в определённом порядке. Чаще
4 // всего эти методы являются шагами некоего алгоритма.
5 method turn() is
6 collectResources()
7 buildNewStructures()
8 buildUnits()
9 attack()
10
11 // Некоторые из этих методов могут быть реализованы прямо в
12 // базовом классе.
13 method collectResources() is
14 foreach (s in this
this.builtStructures) do
15 s.collect()
16
17 // А некоторые могут быть полностью абстрактными.
18 abstract method buildStructures()
19 abstract method buildUnits()
20
21 // Кстати, шаблонных методов в классе может быть несколько.
22 method attack() is
23 enemy = closestEnemy()
24 if (enemy == null
null)
25 sendScouts(map.center)
26 else
[email protected] (#11242)
380 Поведенческие паттерны / Шаблонный метод #11242
27 sendWarriors(enemy.position)
28
29 abstract method sendScouts(position)
30 abstract method sendWarriors(position)
31
32 // Подклассы могут предоставлять свою реализацию шагов
33 // алгоритма, не изменяя сам шаблонный метод.
34 class OrcsAI extends GameAI is
35 method buildStructures() is
36 if (there are some resources) then
37 // Строить фермы, затем бараки, а потом цитадель.
38
39 method buildUnits() is
40 if (there are plenty of resources) then
41 if (there are no scouts)
42 // Построить раба и добавить в группу
43 // разведчиков.
44 else
45 // Построить пехотинца и добавить в группу
46 // воинов.
47
48 // ...
49
50 method sendScouts(position) is
51 if (scouts.length > 0) then
52 // Отправить разведчиков на позицию.
53
54 method sendWarriors(position) is
55 if (warriors.length > 5) then
56 // Отправить воинов на позицию.
57
58
[email protected] (#11242)
381 Поведенческие паттерны / Шаблонный метод #11242
Применимость
Когда подклассы должны расширять базовый алгоритм, не
меняя его структуры.
[email protected] (#11242)
382 Поведенческие паттерны / Шаблонный метод #11242
Шаги реализации
1. Изучите алгоритм и подумайте, можно ли его разбить на
шаги. Прикиньте, какие шаги будут стандартными для всех
вариаций алгоритма, а какие — изменяющимися.
[email protected] (#11242)
383 Поведенческие паттерны / Шаблонный метод #11242
Преимущества и недостатки
Облегчает повторное использование кода.
[email protected] (#11242)
384 Поведенческие паттерны / Посетитель #11242
ПОСЕТИТЕЛЬ
Также известен как: Visitor
[email protected] (#11242)
385 Поведенческие паттерны / Посетитель #11242
Проблема
Ваша команда разрабатывает приложение, работающее с
геоданными в виде графа. Узлами графа являются городские
локации: памятники, театры, рестораны, важные предприя-
тия и прочее. Каждый узел имеет ссылки на другие, ближай-
шие к нему узлы. Каждому типу узлов соответствует свой
класс, а каждый узел представлен отдельным объектом.
[email protected] (#11242)
386 Поведенческие паттерны / Посетитель #11242
Решение
Паттерн Посетитель предлагает разместить новое поведе-
ние в отдельном классе, вместо того чтобы множить его
сразу в нескольких классах. Объекты, с которыми должно
было быть связано поведение, не будут выполнять его само-
[email protected] (#11242)
387 Поведенческие паттерны / Посетитель #11242
[email protected] (#11242)
388 Поведенческие паттерны / Посетитель #11242
1 // Client code
2 foreach (Node node in graph)
3 node.accept(exportVisitor)
4
5 // City
6 class City is
7 method accept(Visitor v) is
8 v.doForCity(this
this)
9 // ...
10
11 // Industry
12 class Industry is
13 method accept(Visitor v) is
14 v.doForIndustry(this
this)
15 // ...
[email protected] (#11242)
389 Поведенческие паттерны / Посетитель #11242
Аналогия из жизни
[email protected] (#11242)
390 Поведенческие паттерны / Посетитель #11242
Структура
[email protected] (#11242)
391 Поведенческие паттерны / Посетитель #11242
[email protected] (#11242)
392 Поведенческие паттерны / Посетитель #11242
Псевдокод
В этом примере Посетитель добавляет в существующую
иерархию классов геометрических фигур возможность экс-
порта в XML.
[email protected] (#11242)
393 Поведенческие паттерны / Посетитель #11242
[email protected] (#11242)
394 Поведенческие паттерны / Посетитель #11242
[email protected] (#11242)
395 Поведенческие паттерны / Посетитель #11242
69 method export() is
70 exportVisitor = new XMLExportVisitor()
71
72 foreach (shape in allShapes) do
73 shape.accept(exportVisitor)
Применимость
Когда вам нужно выполнить какую-то операцию над всеми
элементами сложной структуры объектов, например,
деревом.
[email protected] (#11242)
396 Поведенческие паттерны / Посетитель #11242
Шаги реализации
1. Создайте интерфейс посетителя и объявите в нём методы
«посещения» для каждого класса элемента, который суще-
ствует в программе.
[email protected] (#11242)
397 Поведенческие паттерны / Посетитель #11242
Преимущества и недостатки
Упрощает добавление операций, работающих со сложными
структурами объектов.
Объединяет родственные операции в одном классе.
[email protected] (#11242)
398 Поведенческие паттерны / Посетитель #11242
[email protected] (#11242)
#11242
Заключение
Поздравляю! Вы добрались до конца!
Но в мире существует множество других паттернов. Наде-
юсь, эта книга станет вашей стартовой точкой к дальнейше-
му постижению паттернов и развитию сверхспособностей в
проектировании программ.
[email protected] (#11242)