→ Пошук по сайту       Увійти / Зареєструватися
Знання Патерни Структурні патерни — Structural patterns

11. Легковаговик — Flyweight

11.	Легковаговик — Flyweight

Уявіть, що ви розробляєте якогось бота до онлайн-іграшки. У вас уже є веб-клієнт, який на кожну відповідь від сервера парсить HTML, у якому є записані юніти гри. Гра має близько 50 різних типів юнітів-тваринок, але коли ви розбираєте відповідь, то ви можете отримати цілу гору екземплярів однієї і тієї ж тваринки і ще цілу купу екземплярів інших тварюк.

Якщо ваш бот шпіляє дуже інтенсивно, то аплікація буде просто заганятися кожного разу створювати велику кількість інстансів кожного із юнітів. Але що цікаво, самі юніти мають одні і ті ж початкові значення здоров’я та одне й те ж саме зображення. Звичайно, із плином гри здоров’я зменшується, але зображення, що візуалізує тварюку, одне й те ж саме!

Це може призвести до неефективного використання пам’яті. То як ми можемо зробити загальну інформацію про зображення тваринки (і т.п.) спільною для кожного окремого юніта одного типу?

Флайвейт забезпечує підтримку великої кількості об’єктів шляхом виокремлення спільної інформації для збереження в одному екземплярі.

Цього разу приклад буде побудовайний по іншому – ми розглянемо декілька варінтів вирішення проблеми.

Перший варіант із створенням об’єктів зображення для кожного юніта

Уривок коду 11.1. Базовий клас для юнітів

 abstract class Unit
{
    public string Name { get; protected set; }
    public int Health { get; protected set; }
    public Image Picture { get; protected set; }
}

І два породжені класи, які зображають гобліна (Goblin) та дракона (Dragon) з їхніми початковими значеннями здоров’я (Health) та зображення (Picture). Наведемо код гобліна. Тут слід звернути увагу на те, що зображення гобліна є дуже велике, тому буде займати багато пам’яті.

Уривок коду 11.2. Код гобліна

 class Goblin : Unit
{
    public Goblin()
    {
        Name = "Goblin";
        Health = 8;
        Picture = UnitImagesFactory.CrateGoblinImage();
    }
}

Уривок коду 11.3. Парсер насправді імітує якусь роботу по створенню об'єктів

 class Parser
{
    public List<Unit> ParseHTML()
    {
        var units = new List<Unit>();
        for (int i = 0; i < 150; i++)
            units.Add(new Dragon());
        for (int i = 0; i < 500; i++)
            units.Add(new Goblin());
        Console.WriteLine("Dragons and Goblins are parsed from HTML page.");
        return units;
    }
}

То ж ми створили 150 Драконів і 500 Гоблінів. Це забрало нам аж... 439 Mb.

Малюнок 2. Пам’ять зайнята юнітами гри без застосування патерну Флайвейт

Другий варіант. То як же Флайвейт працює? (не думаю, що когось пів гіга зжертої пам'яті влаштовує)

Просто введемо новий клас, який буде фабрикою зображень. У нашому випадку зображення і буде флайвейт об’єктом. Але варто зауважити, що насправді замість зображення ми б могли «шарити» більше інформації, скажімо, ми б мали базовий клас UnitInitialInfo, який був би полем у класі Unit, а потім фабрика видавали б нам конкретні реалізації цього Info класу. В нашому прикладі ми наводимо тільки створення «імейджу» для різних тваринок, причому якщо зображення уже завантажувалося для істоти, то ми його знову не будемо завантажувати.

Увок коду 11.4. Сховище для зображень

 class UnitImagesFactory
{
    public static Dictionary<Type, Image> Images = new Dictionary<Type, Image>();
    public static Image CrateDragonImage()
    {
        if (!Images.ContainsKey(typeof(Dragon)))
        {
            Images.Add(typeof(Dragon), Image.Load("Dragon.jpg"));
        }
        return Images[typeof(Dragon)];
    }
    public static Image CrateGoblinImage()
    {
        if (!Images.ContainsKey(typeof(Goblin)))
        {
            Images.Add(typeof(Goblin), Image.Load("Goblin.jpg"));
        }
        return Images[typeof(Goblin)];
    }
}

Увок коду 11.5. Конструктори Гобліна і Дракона дещо змінені для використання нашої фабрики

 class Dragon : Unit
{
    public Dragon()
    {
        Name = "Dragon";
        Health = 50;
        // От власне те, що змінилося від попередньої версії
        Picture = UnitImagesFactory.CrateDragonImage();
    }
}

Також глянемо на UML діаграму.

UML-діаграма 7. Патерн Флайвейт

Ця UML діаграма не відповідає класичній діаграмі із GoF книжки, але треба сказати, що нам слід бути до цього готовими. В реальному світі реалізація патерну часто відрізняється від того, що описано у всіма відомій книзі. Можна дуже дивуватися тому, що люди чітко пробують дотриматися такої ж структури - часто вона буває занадто загальною. Як на мене, то в оригінальній статті про Flyweight не було очевидним, що Goblin та Dragon є репрезентаціями Info-класів для тваринок.

Малюнок 3. Тепер в ран-таймі наш чудо-бот зжерає тільки 7 Mb

Як видно із малюнка вище, ми домоглися скорочення споживання пам’яті нашою програмою-ботом.

По матеріалам книги Андрія Будая "Дизайн патерни – просто, як двері". Матеріал розміщується за домовленістю з автором.
Робота представлена за умовами ліцензії Creative Commons Attribution-NonCommercial 3.0 Unported License.

загрузка...
Сторінки, близькі за змістом