Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Design Patterns via C#.pdf
Скачиваний:
154
Добавлен:
17.03.2016
Размер:
13.25 Mб
Скачать

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 в виде работы подсистемы управления памятью и особенностью техники разделения исполняемых сущностей на экземпляры и объекты.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]