Приспособленец (Flyweight)
реализации: java, количество: 9
реализации(исходники)
+добавить
Приспособленец - паттерн, структурирующий объекты таким образом, что из них инстанцируется всего лишь ограниченный необходимый набор экземпляров вместо всего большого множества. codelab.ru codelab.ru источник оригинал
Условия, Задача, Назначение
Решает задачу предотвращения инстанцирования большого количества объектов, требуемого для представления всех сущностей системы. оригинал codelab.ru codelab.ru источникПаттерн приспособленец предлагает одно из решений данной проблемы, вполне уже известное в истории ПО, но в несколько другом исполнении – речь идет о пуле объектов.
Здесь подход пула предлагается применять следующим образом: у объектов этого огромного множества выделяется фиксированное число их состояний, которые они принимают в процессе своей деятельности, на каждое такое состояние создается по специальному объекту и далее в процессе функционирования в программе инстанцируется и фигурирует только это ограниченное количество экземпляров; когда нужен снова подобный объект, клиент выбирает из этого списка подходящий, создает на него ссылку, и когда наступает черед чтобы этот объект выполнил свои собственные задачи – клиент передает ему все другие необходимые для работы данные и запускает эти его общие разделяемые другими клиентами методы.
Мотивация
Для дальнейшего понимания рассмотрим опять же ситуацию с текстовыми редакторами. Например, в большинстве редакторов документов имеются средства форматирования и редактирования текстов, в той или иной степени модульные. Объектно-ориентированные редакторы обычно применяют объекты для представления таких встроенных элементов, как таблицы и рисунки. Но они не используют объекты для представления каждого символа, несмотря на то, что это увеличило бы гибкость на самых нижних уровнях приложения. Ведь тогда к рисованию и форматированию символов и встроенных элементов можно было бы применить единообразный подход (см. паттерн Composite). И для поддержки новых наборов символов не пришлось бы как-либо затрагивать остальные функции редактора. Да и общая структура приложения отражала бы физическую структуру документа. На следующей диаграмме показано, как редактор документов мог бы воспользоваться объектами для представления символов: источник оригинал codelab.ru codelab.ru
оригинал источник codelab.ru codelab.ru
У такого дизайна есть один недостаток - стоимость. Даже в документе скромных размеров было бы несколько сотен тысяч объектов-символов, а это привело бы к расходованию огромного объема памяти и неприемлемым затратам во время выполнения. Паттерн приспособленец показывает, как разделять очень мелкие объекты без недопустимо высоких издержек. codelab.ru оригинал источник codelab.ru
codelab.ru источник codelab.ru оригинал
codelab.ru codelab.ru оригинал источник
codelab.ru источник оригинал codelab.ru
Признаки применения, использования паттерна Приспособленец (Flyweight)
Эффективность паттерна приспособленец во многом зависит от того, как и где он используется. Применяйте этот паттерн, когда выполнены все нижеперечисленные условия: оригинал codelab.ru источник codelab.ru- В приложении используется большое число объектов и из-за этого накладные расходы на хранение высоки. codelab.ru codelab.ru оригинал источник
- Большую часть состояния объектов можно вынести вовне (в ответственность клиентов, которые создают и используют эти объекты). codelab.ru codelab.ru источник оригинал
- Многие группы объектов можно заменить относительно небольшим количеством разделяемых объектов, поскольку внешнее состояние вынесено. codelab.ru codelab.ru источник оригинал
- Приложение не зависит от идентичности объекта. Поскольку объекты-приспособленцы могут разделяться, то проверка на идентичность возвратит истину для «концептуально» различных объектов (внутреннее состояние объектов же не меняется). Ключевой момент в том, что когда эти объекты запускают свои функции, то с учетом переданной от клиента информации (внешнего состояния) – результат выполнения получения совсем не идентичен другим. оригинал источник codelab.ru codelab.ru
Решение
источник codelab.ru оригинал codelab.ru
источник оригинал codelab.ru codelab.ru
codelab.ru codelab.ru оригинал источник
Участники паттерна Приспособленец (Flyweight)
codelab.ru оригинал codelab.ru источник- Flyweight (Glyph) – приспособленец.
Объявляет интерфейс, с помощью которого приспособленцы могут получать внешнее состояние или как-то воздействовать на него. codelab.ru оригинал codelab.ru источник - ConcreteFlyweight (Character) - конкретный приспособленец.
Реализует интерфейс класса Flyweight и добавляет при необходимости внутреннее состояние. Объект класса ConcreteFlyweight должен быть разделяемым. Любое сохраняемое им состояние должно быть внутренним, то есть не зависящим от контекста. источник codelab.ru codelab.ru оригинал - UnsharedConcreteFlyweight (Row, Column) - неразделяемый конкретный приспособленец.
Не все подклассы Flyweight обязательно должны быть разделяемыми. Интерфейс Flyweight допускает разделение, но не навязывает его. Часто у объектов UnsharedConcreteFlyweight на некотором уровне структуры приспособленца есть потомки в виде объектов класса ConcreteFlyweight, как, например, у объектов классов Row и Column. codelab.ru оригинал источник codelab.ru - FlyweightFactory - фабрика приспособленцев.
Создает объекты-приспособленцы и управляет ими.
Обеспечивает должное разделение приспособленцев. Когда клиент запрашивает приспособленца, объект FlyweightFactory предоставляет существующий экземпляр или создает новый, если готового еще нет. оригинал codelab.ru источник codelab.ru - Client – клиент.
Хранит ссылки на одного или нескольких приспособленцев.
Вычисляет или хранит внешнее состояние приспособленцев. оригинал источник codelab.ru codelab.ru
Схема использования паттерна Приспособленец (Flyweight)
Cостояние, необходимое приспособленцу для нормальной работы, можно охарактеризовать как внутреннее или внешнее. Первое хранится в самом объекте ConcreteFlyweight. Внешнее состояние хранится или вычисляется клиентами. Клиент передает его приспособленцу при вызове операций. codelab.ru codelab.ru источник оригиналКлиенты не должны создавать экземпляры класса ConcreteFlyweight напрямую, а могут получать их только от объекта FlyweightFactory. Это позволит гарантировать корректное разделение. источник codelab.ru codelab.ru оригинал
Вопросы, касающиеся реализации паттерна Приспособленец (Flyweight)
При реализации приспособленца следует обратить внимание на следующие вопросы: codelab.ru codelab.ru оригинал источник- Вынесение внешнего состояния.
Применимость паттерна в значительной степени зависит от того, насколько легко можно «очертить» внешнее состояние и вынести его за пределы разделяемых объектов. Вынесение внешнего состояния не уменьшает стоимости хранения, если различных внешних состояний так же много, как и объектов до разделения. Лучший вариант - внешнее состояние вычисляется по объектам с другой структурой, требующей значительно меньшей памяти.
Например, в нашем редакторе документов мы можем поместить карту с типографской информацией в отдельную структуру, а не хранить шрифт и начертание вместе с каждым символом. Данная карта будет отслеживать непрерывные серии символов с одинаковыми типографскими атрибутами. Когда объект-символ изображает себя, он получает типографские атрибуты от алгоритма обхода. Поскольку обычно в документах используется немного разных шрифтов и начертаний, то хранить эту информацию отдельно от объекта-символа гораздо эффективнее, чем непосредственно в нем. оригинал codelab.ru источник codelab.ru - Управление разделяемыми объектами.
Так как объекты разделяются, клиенты не должны инстанцировать их напрямую. Фабрика FlyweightFactory позволяет клиентам найти подходящего приспособленца. В объектах этого класса часто есть хранилище, организованное в виде ассоциативного массива (или хеш-таблицы), с помощью которого можно быстро находить приспособленца, нужного клиенту. Так, в примере редактора документов фабрика приспособленцев может содержать внутри себя таблицу, индексированную кодом символа, и возвращать нужного приспособленца по его коду. А если требуемый приспособленец отсутствует, он тут же создается. Разделяемость подразумевает также, что имеется некоторая форма подсчета ссылок или сбора мусора для освобождения занимаемой приспособленцем памяти, когда необходимость в нем отпадает. Однако ни то, ни другое необязательно, если число приспособленцев фиксировано и невелико (например, если речь идет о представлении набора символов кода ASCII). В таком случае имеет смысл хранить приспособленцев в памяти постоянно. оригинал codelab.ru codelab.ru источник
Результаты
При использовании приспособленцев не исключены затраты на передачу, поиск или вычисление внешнего состояния, особенно если раньше оно хранилось как внутреннее. Однако такие расходы с лихвой компенсируются экономией памяти за счет разделения объектов-приспособленцев. codelab.ru codelab.ru источник оригинал- Уменьшение общего числа экземпляров. codelab.ru оригинал источник codelab.ru
- Сокращение объема памяти, необходимого для хранения внутреннего состояния. источник codelab.ru оригинал codelab.ru
- Вычисление, а не хранение внешнего состояния. источник оригинал codelab.ru codelab.ru
Чем выше степень разделения приспособленцев, тем существеннее экономия. С увеличением объема разделяемого состояния экономия также возрастает. Самого большого эффекта удается добиться, когда суммарный объем внутренней и внешней информации о состоянии велик, а внешнее состояние вычисляется, а не хранится. Тогда разделение уменьшает стоимость хранения внутреннего состояния, а за счет вычислений сокращается память, отводимая под внешнее состояние. оригинал источник codelab.ru codelab.ru
Паттерн приспособленец часто применяется вместе с компоновщиком для представления иерархической структуры в виде графа с разделяемыми листовыми узлами. Из-за разделения указатель на родителя не может храниться в листовом узле-приспособленце, а должен передаваться ему как часть внешнего состояния.
Это оказывает заметное влияние на способ взаимодействия объектов иерархии между собой, конкретно на способы обхода этих структур, т.к. очевидно, что в этом случае мы сможем двигаться только в одном направлении: от верхних к самым внутренним уровням иерархий. оригинал codelab.ru codelab.ru источник
Пример
Представим себе некоторый простенький компоновщик содержимого web-контента. Html-страница может состоять из огромного количества html-тегов: a, p, div, img и т.д. Если при этом создаются отдельные объекты для каждого тега на странице – при больших объемах обязательно возникнет проблема перерасхода памяти на хранение всех них. оригинал codelab.ru codelab.ru источникВот как будет выглядеть базовый класс для всех тегов (Flyweight): HtmlTag. источник оригинал codelab.ru codelab.ru
Класс контекста: TagContext. источник оригинал codelab.ru codelab.ru
Как видим – содержит внешнее состояние. Здесь для простоты это всего лишь список css-стилей: CssStyle. codelab.ru codelab.ru источник оригинал
Все используемые на странице теги (ConcreteFlyweights) будут иметь свои классы объектов, наследуемые от базового HtmlTag: ATag, PTag, ImgTag, DivTag и т.д. источник codelab.ru codelab.ru оригинал
Далее, соответственно, фабрика тегов (FlyweightFactory): TagFactory. оригинал codelab.ru codelab.ru источник
После определения всех необходимых составляющих паттерна, можно представить клиента (Client), который, используя фабрику, просто будет как ему угодно составлять т.н. “layout”(верстка, каркас) html-страницы: Client. источник codelab.ru оригинал codelab.ru
Известные применения паттерна Приспособленец (Flyweight)
Концепция объектов-приспособленцев впервые была описана и использована как техника проектирования в библиотеке Interviews 3.0. Ее разработчики построили мощный редактор документов Doc, чтобы доказать практическую полезность подобной идеи. В Doc объекты-глифы используются для представления любого символа документа. Редактор строит по одному экземпляру глифа для каждого сочетания символа и стиля (в котором определены все графические атрибуты). Таким образом, внутреннее состояние символа состоит из его кода и информации о стиле (индекс в таблицу стилей). Следовательно, внешней оказывается только позиция, поэтому Doc работает быстро. Документы представляются классом Document, который выполняет функции фабрики FlyweightFactory. Измерения показали, что реализованное в Doc разделение символов-приспособленцев весьма эффективно. В типичном случае для документа из 180 тысяч знаков необходимо создать только 480 объектов-символов. источник оригинал codelab.ru codelab.ruВ каркасе ЕТ++ приспособленцы используются для поддержки независимости от внешнего облика. Его стандарт определяет расположение элементов пользовательского интерфейса (полос прокрутки, кнопок, меню и пр., в совокупности именуемых виджетами) и их оформления (тени и т.д.). Виджет делегирует заботу о своем расположении и изображении отдельному объекту Layout. Изменение этого объекта ведет к изменению внешнего облика даже во время выполнения. codelab.ru оригинал источник codelab.ru
Родственные паттерны
Паттерн приспособленец часто используется в сочетании с компоновщиком для реализации иерархической структуры в виде ациклического направленного графа с разделяемыми листовыми вершинами. оригинал источник codelab.ru codelab.ruРеализации: java(9) +добавить реализацию
1) HtmlTag.java, code #470[автор:this]
2) TagContext.java, code #471[автор:this]
3) CssStyle.java, code #472[автор:this]
4) ATag.java, code #473[автор:this]
5) PTag.java, code #474[автор:this]
6) ImgTag.java, code #475[автор:this]
7) DivTag.java, code #476[автор:this]
8) TagFactory.java, code #477[автор:this]
9) Client.java, code #478[автор:this]



