- •Предисловие 15
- •Предисловие
- •От авторов
- •Об авторах
- •Благодарности
- •Принятые в книге обозначения
- •Технические рекомендации
- •Дополнительные ресурсы
- •Глава 1. Введение
- •1.1. Понятие паттерна проектирования
- •Определение
- •Метафора
- •1.2. Формат описания паттернов проектирования
- •Название
- •Также известен как
- •Классификация
- •Частота использования
- •Назначение
- •Введение
- •Структура паттерна на языке UML
- •Структура паттерна на языке C#
- •Участники
- •Отношения между участниками
- •Мотивация
- •Применимость паттерна
- •Результаты
- •Реализация
- •Пример кода
- •Известные применения паттерна в .Net
- •1.3. Каталог паттернов проектирования
- •Порождающие
- •Структурные
- •Поведенческие
- •1.4. Техники ООП
- •Фабрика - Продукт
- •Фасад - Подсистема
- •Диспетчеризация
- •Цепочка объектов
- •Издатель-Подписчик
- •1.5. Принципы организации каталога
- •Цель паттерна
- •Уровень паттерна
- •1.6. Рекомендации по изучению паттернов
- •1.7. Рекомендации по применению паттернов
- •Глава 2. Порождающие паттерны
- •Игра - Лабиринт
- •Паттерн Abstract Factory
- •Название
- •Также известен как
- •Классификация
- •Частота использования
- •Назначение
- •Введение
- •Структура паттерна на языке UML
- •Структура паттерна на языке C#
- •Участники
- •Отношения между участниками
- •Отношения между классами
- •Отношения между объектами
- •Мотивация
- •Применимость паттерна
- •Результаты
- •Реализация
- •Пример кода игры «Лабиринт»
- •Известные применения паттерна в .Net
- •Паттерн Builder
- •Название
- •Также известен как
- •Классификация
- •Частота использования
- •Назначение
- •Введение
- •Структура паттерна на языке UML
- •Структура паттерна на языке C#
- •Участники
- •Отношения между участниками
- •Отношения между классами
- •Отношения между объектами
- •Мотивация
- •Применимость паттерна
- •Результаты
- •Реализация
- •Пример кода игры «Лабиринт»
- •Известные применения паттерна в .Net
- •Паттерн Factory Method
- •Название
- •Также известен как
- •Классификация
- •Частота использования
- •Назначение
- •Введение
- •Структура паттерна на языке UML
- •Структура паттерна на языке C#
- •Участники
- •Отношения между участниками
- •Отношения между классами
- •Отношения между объектами
- •Мотивация
- •Применимость паттерна
- •Результаты
- •Реализация
- •Пример кода игры «Лабиринт»
- •Известные применения паттерна в .Net
- •Паттерн Prototype
- •Название
- •Также известен как
- •Классификация
- •Частота использования
- •Назначение
- •Введение
- •Структура паттерна на языке UML
- •Структура паттерна на языке C#
- •Участники
- •Отношения между участниками
- •Отношения между классами
- •Отношения между объектами
- •Мотивация
- •Применимость паттерна
- •Результаты
- •Реализация
- •Пример кода игры «Лабиринт»
- •Известные применения паттерна в .Net
- •Паттерн Singleton
- •Название
- •Также известен как
- •Классификация
- •Частота использования
- •Назначение
- •Введение
- •Структура паттерна на языке UML
- •Структура паттерна на языке C#
- •Участники
- •Отношения между участниками
- •Отношения между классами
- •Отношения между объектами
- •Мотивация
- •Применимость паттерна
- •Результаты
- •Реализация
- •Пример кода игры «Лабиринт»
- •Известные применения паттерна в .Net
- •Глава 3. Структурные паттерны
- •Паттерн Adapter
- •Название
- •Также известен как
- •Классификация
- •Частота использования
- •Назначение
- •Введение
- •Структура паттерна на языке UML
- •Adapter уровня классов
- •Adapter уровня объектов
- •Структура паттерна на языке C#
- •Adapter уровня классов
- •Adapter уровня объектов
- •Участники
- •Отношения между участниками
- •Отношения между классами (для адаптера уровня классов)
- •Отношения между классами (для адаптера уровня объектов)
- •Отношения между объектами
- •Мотивация
- •Применимость паттерна
- •Результаты
- •Adapter уровня классов
- •Adapter уровня объектов
- •Особенности применения паттерна Adapter
- •Реализация
- •Пример кода
- •Известные применения паттерна в .Net
- •Паттерн Bridge
- •Название
- •Также известен как
- •Классификация
- •Частота использования
- •Назначение
- •Введение
- •Структура паттерна на языке UML
- •Структура паттерна на языке C#
- •Участники
- •Отношения между участниками
- •Отношения между классами
- •Отношения между объектами
- •Мотивация
- •Применимость паттерна
- •Результаты
- •Реализация
- •Пример кода
- •Паттерн Composite
- •Название
- •Также известен как
- •Классификация
- •Частота использования
- •Назначение
- •Введение
- •Структура паттерна на языке UML
- •Структура паттерна на языке C#
- •Участники
- •Отношения между участниками
- •Отношения между классами
- •Отношения между объектами
- •Мотивация
- •Применимость паттерна
- •Результаты
- •Реализация
- •Известные применения паттерна в .Net
- •Паттерн Decorator
- •Название
- •Также известен как
- •Классификация
- •Частота использования
- •Назначение
- •Введение
- •Структура паттерна на языке UML
- •Структура паттерна на языке C#
- •Участники
- •Отношения между участниками
- •Отношения между классами
- •Отношения между объектами
- •Мотивация
- •Применимость паттерна
- •Результаты
- •Реализация
- •Известные применения паттерна в .Net
- •Паттерн Facade
- •Название
- •Также известен как
- •Классификация
- •Частота использования
- •Назначение
- •Введение
- •Структура паттерна на языке UML
- •Структура паттерна на языке C#
- •Участники
- •Отношения между участниками
- •Отношения между классами
- •Отношения между объектами
- •Мотивация
- •Применимость паттерна
- •Результаты
- •Реализация
- •Пример кода
- •Паттерн Flyweight
- •Название
- •Также известен как
- •Классификация
- •Частота использования
- •Назначение
- •Введение
- •Структура паттерна на языке UML
- •Структура паттерна на языке C#
- •Участники
- •Отношения между участниками
- •Отношения между классами
- •Отношения между объектами
- •Мотивация
- •Применимость паттерна
- •Результаты
- •Реализация
- •Пример кода
- •Известные применения паттерна в .Net
- •Паттерн Proxy
- •Название
- •Также известен как
- •Классификация
- •Частота использования
- •Назначение
- •Введение
- •Структура паттерна на языке UML
- •Структура паттерна на языке C#
- •Участники
- •Отношения между участниками
- •Отношения между классами
- •Отношения между объектами
- •Мотивация
- •Применимость паттерна
- •Результаты
- •Реализация
- •Пример кода
- •Известные применения паттерна в .Net
- •Глава 4. Паттерны поведения
- •Паттерн Chain of Responsibility
- •Название
- •Также известен как
- •Классификация
- •Частота использования
- •Назначение
- •Введение
- •Структура паттерна на языке UML
- •Структура паттерна на языке C#
- •Участники
- •Отношения между участниками
- •Отношения между классами
- •Отношения между объектами
- •Мотивация
- •Применимость паттерна
- •Результаты
- •Реализация
- •Пример кода
- •Паттерн Command
- •Название
- •Также известен как
- •Классификация
- •Частота использования
- •Назначение
- •Введение
- •Структура паттерна на языке UML
- •Структура паттерна на языке C#
- •Участники
- •Отношения между участниками
- •Отношения между классами
- •Отношения между объектами
- •Мотивация
- •Применимость паттерна
- •Результаты
- •Реализация
- •Пример кода
- •Известные применения паттерна в .Net
- •Паттерн Interpreter
- •Название
- •Также известен как
- •Классификация
- •Частота использования
- •Назначение
- •Введение
- •Структура паттерна на языке UML
- •Структура паттерна на языке C#
- •Участники
- •Отношения между участниками
- •Отношения между классами
- •Отношения между объектами
- •Паттерн Iterator
- •Название
- •Также известен как
- •Классификация
- •Частота использования
- •Назначение
- •Введение
- •Структура паттерна на языке UML
- •Классическое представление
- •Представление Microsoft .NET
- •Структура паттерна на языке C#
- •Классическое представление
- •Представление Microsoft .NET
- •Участники
- •Отношения между участниками
- •Отношения между классами
- •Отношения между объектами
- •Мотивация
- •Применимость паттерна
- •Результаты
- •Реализация
- •Известные применения паттерна в .Net
- •Паттерн Mediator
- •Название
- •Также известен как
- •Классификация
- •Частота использования
- •Назначение
- •Введение
- •Структура паттерна на языке UML
- •Структура паттерна на языке C#
- •Участники
- •Отношения между участниками
- •Отношения между классами
- •Отношения между объектами
- •Мотивация
- •Применимость паттерна
- •Результаты
- •Реализация
- •Пример кода
- •Паттерн Memento
- •Название
- •Также известен как
- •Классификация
- •Частота использования
- •Назначение
- •Введение
- •Структура паттерна на языке UML
- •Структура паттерна на языке C#
- •Участники
- •Отношения между участниками
- •Отношения между классами
- •Отношения между объектами
- •Мотивация
- •Применимость паттерна
- •Реализация
- •Паттерн Observer
- •Название
- •Также известен как
- •Классификация
- •Частота использования
- •Назначение
- •Введение
- •Структура паттерна на языке UML
- •Модель вытягивания (Pull model)
- •Модель проталкивания (Push model)
- •Структура паттерна на языке C#
- •Модель вытягивания (Pull model)
- •Модель проталкивания (Push model)
- •Участники
- •Отношения между участниками
- •Отношения между классами
- •Отношения между объектами
- •Мотивация
- •Применимость паттерна
- •Результаты
- •Реализация
- •Пример кода
- •Известные применения паттерна в .Net
- •Паттерн State
- •Название
- •Также известен как
- •Классификация
- •Частота использования
- •Назначение
- •Введение
- •Структура паттерна на языке UML
- •Структура паттерна на языке C#
- •Участники
- •Отношения между участниками
- •Отношения между классами
- •Отношения между объектами
- •Мотивация
- •Применимость паттерна
- •Результаты
- •Паттерн Strategy
- •Название
- •Также известен как
- •Классификация
- •Частота использования
- •Назначение
- •Введение
- •Структура паттерна на языке UML
- •Структура паттерна на языке C#
- •Участники
- •Отношения между участниками
- •Отношения между классами
- •Отношения между объектами
- •Применимость паттерна
- •Паттерн Template Method
- •Название
- •Также известен как
- •Классификация
- •Частота использования
- •Назначение
- •Введение
- •Структура паттерна на языке UML
- •Структура паттерна на языке C#
- •Участники
- •Отношения между участниками
- •Отношения между классами
- •Отношения между объектами
- •Применимость паттерна
- •Результаты
- •Реализация
- •Паттерн Visitor
- •Название
- •Также известен как
- •Классификация
- •Частота использования
- •Назначение
- •Введение
- •Структура паттерна на языке UML
- •Структура паттерна на языке C#
- •Участники
- •Отношения между участниками
- •Отношения между классами
- •Отношения между объектами
- •Применимость паттерна
- •Результаты
- •Реализация
- •Известные применения паттерна в .Net
- •Библиография
151
Мотивация
Некоторые разновидности приложений или их части, не всегда могут быть построены полностью с использованием объектно-ориентированного подхода. Рассмотрим такую разновидность приложений, как текстовые редакторы. Объектно-ориентированные текстовые редакторы, для представления таблиц и рисунков обычно используют объекты соответствующих классов (Table, Image). Но использовать объекты для представления каждого отдельного символа в документе, может оказаться нерациональным с точки зрения расходования памяти, ведь в документе может быть несколько сотен тысяч символов, соответственно в памяти будет создано несколько сотен тысяч объектов класса Character. С другой стороны, использование объектов для представления символов повысило бы гибкость использования и сопровождения приложения.
Паттерн Flyweight показывает, как правильно строить подсистемы, в которых требуется использовать очень большое число объектов и при этом избежать недопустимо высокого расходования памяти. На рисунке показано, как редактор документов мог бы использовать объекты для представления символов.
Для построения редактора документов потребуется создать следующие классы: класс Page – представляющий страницу, класс Column – представляющий колонки (столбцы) на которые может быть разбита страница, класс Row – представляющий строки, которые содержатся в колонках и класс Character представляющий символы, находящиеся в строках.
Page page
|
Column |
|
|
Column |
|
|
column1 |
|
|
column2 |
|
Row |
Row |
Row |
Row |
Row |
Row |
row |
row |
row |
row |
row |
row |
A P P A R E N T
Для того чтобы избежать построения тысяч экземпляров класса Character, потребуется использовать так называемые разделяемые объекты. Разделяемый объект – это такой объект, которым могут (одновременно) пользоваться другие объекты.
152
Например, в жизни, один актер может играть несколько ролей на протяжении спектакля, выходя на сцену в разных костюмах. У зрителя будет создаваться иллюзия, что роли играют разные актеры. Такой актер одновременно (в сеансе спектакля) разделяется на разные роли. Назовем такого актера – разделяемым актером. Тех актеров, которые играют в сеансе спектакля только одну роль, назовем –
неразделяемыми актерами.
Пример из виртуальной реальности: В этой строке имеется только одна буква «а», одна буква «о», одна буква «т» и так далее, не смотря на то, что кажется, что этих букв много. Более того, во всей этой книге имеется только одна маленькая русская буква «а». И в этой строке: а, а, а, - это все одна и та-же буква «а», в разных стилях (размер, тип шрифта, наклон и так далее).
Возникает вопрос: Как возможно добиться такого эффекта в своих программах? Предлагается рассмотреть диаграмму объектов, которые могут входить в состав текстового редактора:
На диаграмме показана древообразная структура объектов, описывающая представление элементов в текстовом документе. Условимся, что представленный на диаграмме документ состоит из одной страницы - объект класса Page. Страница состоит только из одной колонки – объект класса Column (например, страницы данной книги состоят из одной колонки). В колонке имеется три строки – объекты класса Row (row1, row2, row3). Во второй строке содержится набор символов (ссылок на экземпляры класса Character) формирующих слово «apparent». Также на диаграмме показан «пул приспособленцев». Пул – означает некий набор (коллекцию). Приспособленец (Flyweight) – это сленговое название разделяемого объекта. Иначе можно сказать, что на диаграмме показан набор разделяемых объектов класса Character.
Важно понимать, что использование разделяемых объектов – это иллюзия в программировании, как иллюзия в жизни, когда фокусник из кармана достает много раз один и тот же шарик, который (хитрым способом) через трубку в рукаве скатывается обратно в карман. Шарик – разделяемый объект. Трубка, рукав, карман – неразделяемые объекты. Фокус с шариком, не получится без трубки. Для работы с разделяемыми объектами понадобится использование неразделяемых объектов. В примере с текстовым редактором, символ (Character) – разделяемый объект, а страницы (Page), колонки (Column) и строки
(Row) – неразделяемые объекты.
Создавать полноценную иллюзию того, что в документе имеется много экземпляров класса Character, помогает специальный объект класса Graphics, который занимается рисованием фигур и символов в области формы (Form).
Graphics graphics = window.CreateGraphics(); graphics.DrawString(character, font, brush, X, Y);
Экземпляр класса Graphics, еще называют графическим устройством, потому что у него имеется жизненная аналогия – плоттер (устройство которое имеет головку, которая по направляющим передвигается по листу бумаги и вычерчивает рисунки с большой точностью). Также как плоттер, объект класса Graphics «вычерчивает» на виртуальной форме фигуры и символы.
Каждый экземпляр класса Character имеет в себе ссылку на экземпляр класса Graphics и в любой момент может дать команду нарисовать себя на форме. Также класс Character содержит в себе
153
ссылку на объект с настройками стиля символа. Перед рисованием, стиль символа может быть изменен, таким образом один и тот же символ может рисоваться много раз, разными стилями. Основная идея такого подхода заключается в предоставлении возможности разделения состояния объекта-символа на внутреннее (которое хранится непосредственно в объекте-символе) и внешнее (которое подключается как объект со стилями). Такой подход позволяет объекту-приспособленцу разделяться. Внешнее состояние приспособленца считается неразделяемым. На диаграмме классов можно увидеть основных участников, которые входят в состав примера редактора документов.
См. Пример к главе: \011_Flyweight\002_DocumentEditor
Почему в русской интерпретации паттерн Flyweight назван – приспособленцем? Потому что поведение разделяемого объекта напоминает поведение человека – приспособленца. Приспособленец - это человек, который приспосабливается к обстоятельствам, маскируя свои истинные взгляды (чаще в жизни это слово имеет негативный оттенок). Также и разделяемый объект приспосабливается к месту его использования, маскируясь за внешним (меняющимся) состоянием.
Применимость паттерна
Паттерн Flyweight рекомендуется использовать, когда:
В приложении требуется использовать большое количество однотипных объектов и при этом для их хранения требуется выделение большого объема памяти.
Состояние объектов можно разделить на внутреннее и внешнее. Соответственно большие группы объектов можно заменить небольшим количеством разделяемых объектов.
Результаты
При использовании разделяемых объектов (приспособленцев), могут возникнуть затраты на вычисление внешнего состояния, его поиск или передачу, особенно если внешнее состояние ранее использовалось как внутреннее. Вычисления и формирование внешнего состояния обычно производят клиенты, которые используют разделяемый объект. Расходы на вычисление внешнего состояния компенсируются экономией памяти (не требуется дублировать одинаковое состояние в каждом экземпляре разделяемого объекта).
Экономия памяти возникает в следующих случаях:
Уменьшение общего числа экземпляров.
Сокращение объема памяти, необходимого для хранения внутреннего состояния.
Вычисление, а не хранение внутреннего состояния.
154
Чем больше получится создать разделяемых объектов, тем больше будет экономия памяти. Самого большого эффекта экономии памяти возможно добиться, когда внешнее состояние вычисляется, а не хранится, соответственно за счет вычислений сокращается объем выделяемой памяти под хранение внешнего состояния.
Реализация
Полезные приемы реализации паттерна Flyweight:
Вынесение внешнего состояния за пределы разделяемого объекта.
Применимость паттерна Flyweight зависит от того, насколько легко внутреннее состояние вынести во внешнее состояние, за пределы разделяемых объектов. Вынесение состояния не даст преимуществ, если различных состояний также много, как и разделяемых объектов. Оптимальный вариант -организовать вычисление внешнего состояния, а не хранить его в памяти.
Управление разделяемыми объектами.
Клиенты не должны создавать экземпляры разделяемых объектов напрямую через вызов конструкторов. Для создания разделяемого объекта, следует воспользоваться объектом-фабрикой, которая позволит найти необходимого приспособленца. Объект-фабрика содержит в себе ассоциативный массив (ключ-значение), обращаясь к которому по ключу, можно находить нужного приспособленца. Если требуемый приспособленец отсутствует в ассоциативном массиве, то он создается, сохраняется в массиве и клиенту возвращается ссылка на него (нового приспособленца). Также требуется задуматься над созданием механизма по удалению приспособленцев, надобность в которых отпадает. Так как ассоциативный массив в ходе работы программы хранит ссылку на приспособленца, который не будет более использоваться, то механизм сборки мусора не может удалить этого приспособленца, поэтому требуется организовать явное удаление неиспользуемого приспособленца из ассоциативного массива.
Пример кода
Программный код, иллюстрирующий реализацию паттерна на языке C#.
Известные применения паттерна в .Net
Паттерн Flyweight, выражен в платформе .Net в виде работы подсистемы управления памятью и особенностью техники разделения исполняемых сущностей на экземпляры и объекты.