Приспособленец (Flyweight) — различия между версиями
Admin (обсуждение | вклад) (→Диаграмма классов) |
Admin (обсуждение | вклад) (→Назначение) |
||
(не показано 10 промежуточных версий этого же участника) | |||
Строка 1: | Строка 1: | ||
[[Страница_курса_Паттерны_проектирования| К основной странице курса]] | [[Страница_курса_Паттерны_проектирования| К основной странице курса]] | ||
__NOTOC__ | __NOTOC__ | ||
− | |||
=== Назначение === | === Назначение === | ||
+ | Уменьшает количество объектов системы с многочисленными низкоуровневыми особенностями путем совместного использования подобных объектов. | ||
=== Описание === | === Описание === | ||
+ | В некоторых приложениях имеется множество небольших объектов, и задача экономии памяти выходит на первый план. Паттерн Flyweight предназначен для уменьшения количества объектов в системе за счет их совместного использования. Группа объектов может быть объединена в один объект если какая-то часть состояния у них совпадает. Несовпадающую же часть (так называемый внешний контекст) предлагается передавать в качестве параметра операции. | ||
+ | |||
+ | === Использование=== | ||
+ | Паттерн Flyweight можно использовать в случаях когда: | ||
+ | * в приложении имеется множество почти одинаковых объектов | ||
+ | * различающиеся части почти одинаковых объектов можно отделить от одинаковых и заменить эти почти одинаковые объекты одним совместно используемым объектом, передавая различающуюся часть как параметр операции | ||
=== Реализация === | === Реализация === | ||
Строка 11: | Строка 17: | ||
==== Участники==== | ==== Участники==== | ||
− | * | + | * '''Flyweight''' - приспособленец |
+ | Объявляет интерфейс, с помощью которого приспособленцы могут получать внешнее состояние и воздействовать на него | ||
+ | * '''ConcreteFlyweight''' - конкретный приспособленец | ||
+ | Реализует интерфейс Приспособленца и добавляет при необходимости внутреннее состояние. Объект ConcreteFlyweight должен быть разделяемым | ||
+ | * '''FlyweightFactory''' - фабрика приспособленцев | ||
+ | Создает приспособленцев и управляет ими | ||
+ | Когда клиент запрашивает приспособленца, фабрика приспособленцев предоставляет существующий экземпляр или создает новый если готового еще нет. | ||
+ | * '''Context''' - контекст | ||
+ | Внешний контекст для всех приспособленцев. По нему приспособленцы вычисляют своё внешнее состояние | ||
+ | * '''Client''' - клиент | ||
+ | Хранит ссылки на приспособленцев. | ||
+ | Хранит контекст - внешнее состояние приспособленцев. | ||
=== Пример === | === Пример === | ||
− | В графическом редакторе не надо хранить каждый символ в виде объекта. Достаточно хранить по одному объекту для каждого из используемых символов кодовой таблицы и в метод рисования передавать контекст рисования | + | В графическом редакторе не надо хранить каждый символ в виде объекта. Достаточно хранить по одному объекту для каждого из используемых символов кодовой таблицы и в метод рисования передавать контекст рисования. |
− | === Код === | + | |
+ | В данном примере в контексте хранится текущая позиция рисования, которая после каждого рисования символа увеличивается на 1. Кроме того, в контексте хранится набор интервалов. Если символ попадает в один из интервалов, то он рисуется жирным. | ||
+ | |||
+ | Таким образом, у каждого объекта есть внутреннее состояние (символ, который он представляет) и внешнее (позиция в тексте плюс набор интервалов), которое не хранится вместе с объектом, а передается ему как параметр в качестве внешнего контекста. | ||
+ | |||
+ | Приспособленцем здесь выступает символ, который приспосабливается к ситуации в зависимости от контекста. | ||
+ | |||
+ | Без паттерна Приспособленец атрибут жирности символа требовалось бы хранить в классе символа, что при большом количестве символов расточительно. Паттерн Приспособленец позволяет хранить информацию о жирности в виде набора интервалов, что существенно компактнее. Кроме этого, паттерн приспособленец вместо множества объектов, представляющих один символ, хранит лишь один объект. | ||
+ | |||
+ | === Код примера === | ||
<source lang="Csharp"> | <source lang="Csharp"> | ||
+ | class MainApp | ||
+ | { | ||
+ | static void Main() | ||
+ | { | ||
+ | string Str = "AAZZBBZB"; | ||
+ | |||
+ | CharacterFactory factory = new CharacterFactory(); | ||
+ | |||
+ | Context context = new Context(); | ||
+ | context.AddInterval(2, 4); | ||
+ | context.AddInterval(6, 7); | ||
+ | |||
+ | foreach (char c in Str) | ||
+ | { | ||
+ | Character character = factory.GetCharacter(c); // получить приспособленца | ||
+ | character.Draw(context); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | class Pair | ||
+ | { | ||
+ | public int Left,Right; | ||
+ | public Pair(int left, int right) | ||
+ | { | ||
+ | Left = left; | ||
+ | Right = right; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | class Context | ||
+ | { | ||
+ | private int pos = 1; | ||
+ | private List<Pair> list = new List<Pair>(); | ||
+ | public void AddInterval(int left, int right) | ||
+ | { | ||
+ | list.Add(new Pair(left,right)); | ||
+ | } | ||
+ | public void Next() | ||
+ | { | ||
+ | pos++; | ||
+ | } | ||
+ | public bool InInterval() | ||
+ | { | ||
+ | foreach (var p in list) | ||
+ | { | ||
+ | if (pos >= p.Left && pos <= p.Right) | ||
+ | return true; | ||
+ | } | ||
+ | return false; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | class CharacterFactory | ||
+ | { | ||
+ | private Dictionary<char, Character> characters = new Dictionary<char, Character>(); | ||
+ | |||
+ | public Character GetCharacter(char key) | ||
+ | { | ||
+ | Character character = null; | ||
+ | if (characters.ContainsKey(key)) // если есть такой приспособленец | ||
+ | { | ||
+ | character = characters[key]; // то просто вернуть его | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | character = new Character(key); | ||
+ | characters.Add(key, character); // иначе создать, добавить к словарю приспособленцев и вернуть | ||
+ | } | ||
+ | return character; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | class Character | ||
+ | { | ||
+ | private char symbol; | ||
+ | public Character(char sym) | ||
+ | { | ||
+ | symbol = sym; | ||
+ | } | ||
+ | public void Draw(Context context) | ||
+ | { | ||
+ | if (context.InInterval()) | ||
+ | Console.WriteLine("Symbol = {0} BOLD", symbol); // попавшие в один из интервалов контекста символы рисуются жирным | ||
+ | else Console.WriteLine("Symbol = {0}", symbol); | ||
+ | context.Next(); // меняем контекст - позицию символа | ||
+ | } | ||
+ | } | ||
</source> | </source> | ||
=== Достоинства и недостатки === | === Достоинства и недостатки === | ||
− | * | + | * Уменьшение количества обрабатываемых объектов |
− | + | * При вычислении всякий раз контекстной информации теряется производительность | |
− | |||
− | * |
Текущая версия на 14:18, 27 августа 2014
Назначение
Уменьшает количество объектов системы с многочисленными низкоуровневыми особенностями путем совместного использования подобных объектов.
Описание
В некоторых приложениях имеется множество небольших объектов, и задача экономии памяти выходит на первый план. Паттерн Flyweight предназначен для уменьшения количества объектов в системе за счет их совместного использования. Группа объектов может быть объединена в один объект если какая-то часть состояния у них совпадает. Несовпадающую же часть (так называемый внешний контекст) предлагается передавать в качестве параметра операции.
Использование
Паттерн Flyweight можно использовать в случаях когда:
- в приложении имеется множество почти одинаковых объектов
- различающиеся части почти одинаковых объектов можно отделить от одинаковых и заменить эти почти одинаковые объекты одним совместно используемым объектом, передавая различающуюся часть как параметр операции
Реализация
Диаграмма классов
Участники
- Flyweight - приспособленец
Объявляет интерфейс, с помощью которого приспособленцы могут получать внешнее состояние и воздействовать на него
- ConcreteFlyweight - конкретный приспособленец
Реализует интерфейс Приспособленца и добавляет при необходимости внутреннее состояние. Объект ConcreteFlyweight должен быть разделяемым
- FlyweightFactory - фабрика приспособленцев
Создает приспособленцев и управляет ими Когда клиент запрашивает приспособленца, фабрика приспособленцев предоставляет существующий экземпляр или создает новый если готового еще нет.
- Context - контекст
Внешний контекст для всех приспособленцев. По нему приспособленцы вычисляют своё внешнее состояние
- Client - клиент
Хранит ссылки на приспособленцев. Хранит контекст - внешнее состояние приспособленцев.
Пример
В графическом редакторе не надо хранить каждый символ в виде объекта. Достаточно хранить по одному объекту для каждого из используемых символов кодовой таблицы и в метод рисования передавать контекст рисования.
В данном примере в контексте хранится текущая позиция рисования, которая после каждого рисования символа увеличивается на 1. Кроме того, в контексте хранится набор интервалов. Если символ попадает в один из интервалов, то он рисуется жирным.
Таким образом, у каждого объекта есть внутреннее состояние (символ, который он представляет) и внешнее (позиция в тексте плюс набор интервалов), которое не хранится вместе с объектом, а передается ему как параметр в качестве внешнего контекста.
Приспособленцем здесь выступает символ, который приспосабливается к ситуации в зависимости от контекста.
Без паттерна Приспособленец атрибут жирности символа требовалось бы хранить в классе символа, что при большом количестве символов расточительно. Паттерн Приспособленец позволяет хранить информацию о жирности в виде набора интервалов, что существенно компактнее. Кроме этого, паттерн приспособленец вместо множества объектов, представляющих один символ, хранит лишь один объект.
Код примера
class MainApp
{
static void Main()
{
string Str = "AAZZBBZB";
CharacterFactory factory = new CharacterFactory();
Context context = new Context();
context.AddInterval(2, 4);
context.AddInterval(6, 7);
foreach (char c in Str)
{
Character character = factory.GetCharacter(c); // получить приспособленца
character.Draw(context);
}
}
}
class Pair
{
public int Left,Right;
public Pair(int left, int right)
{
Left = left;
Right = right;
}
}
class Context
{
private int pos = 1;
private List<Pair> list = new List<Pair>();
public void AddInterval(int left, int right)
{
list.Add(new Pair(left,right));
}
public void Next()
{
pos++;
}
public bool InInterval()
{
foreach (var p in list)
{
if (pos >= p.Left && pos <= p.Right)
return true;
}
return false;
}
}
class CharacterFactory
{
private Dictionary<char, Character> characters = new Dictionary<char, Character>();
public Character GetCharacter(char key)
{
Character character = null;
if (characters.ContainsKey(key)) // если есть такой приспособленец
{
character = characters[key]; // то просто вернуть его
}
else
{
character = new Character(key);
characters.Add(key, character); // иначе создать, добавить к словарю приспособленцев и вернуть
}
return character;
}
}
class Character
{
private char symbol;
public Character(char sym)
{
symbol = sym;
}
public void Draw(Context context)
{
if (context.InInterval())
Console.WriteLine("Symbol = {0} BOLD", symbol); // попавшие в один из интервалов контекста символы рисуются жирным
else Console.WriteLine("Symbol = {0}", symbol);
context.Next(); // меняем контекст - позицию символа
}
}
Достоинства и недостатки
- Уменьшение количества обрабатываемых объектов
- При вычислении всякий раз контекстной информации теряется производительность