Приспособленец (Flyweight) — различия между версиями

Материал из Вики ИТ мехмата ЮФУ
Перейти к: навигация, поиск
(Диаграмма классов)
(Назначение)
 
(не показано 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 можно использовать в случаях когда:

  • в приложении имеется множество почти одинаковых объектов
  • различающиеся части почти одинаковых объектов можно отделить от одинаковых и заменить эти почти одинаковые объекты одним совместно используемым объектом, передавая различающуюся часть как параметр операции

Реализация

Диаграмма классов

FlyweightCommon.png

Участники

  • 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(); // меняем контекст - позицию символа
    }
}

Достоинства и недостатки

  • Уменьшение количества обрабатываемых объектов
  • При вычислении всякий раз контекстной информации теряется производительность