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

15. Інтерпретер — Interpreter

15.	Інтерпретер — Interpreter

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

Компанія також унікальна тим, що відповідальнісь за рахунок загальної суми покладається на вантажівку. Певним чином вона знає, що повинна додати ціну усіх упаковок, кожна упаковка знає, що вона повинна додати ціну чи то упаковок чи то речей і т.д.

Інтерпретер дозволяє описати граматику певної мови, за допомогою чого можна записати речення на цій мові та інтерпретувати його значення.

Говорячи про граматику нашого дещо «висмоктаного з пальця» прикладу, мовою буде вантажівка/упаковка/різні речі, реченням буде поточне заповнення вантажівки, а значенням речення буде загальна ціна речей всередині.

Існує два види виразів у мові: такі які можна зрозуміти одразу, такі вирази називаються термінальними (terminal expressions), та такі, які вимагають застосування граматичних правил мови. Останні називають нетермінальними виразами (nonterminal expressions).

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

Глянемо на вихідний код прикладу:

Уривок коду 15.1. Ось вирази які використовуються у нашій мові

 // Абстрактний вираз
abstract class Goods
{
    public abstract int Interpret(CurrentPricesContext context);
}

// Нетермінальний вираз (необхідна логіка для визначення значення)
class GoodsPackage : Goods
{
    public List<Goods> GoodsInside { get; set; }
    public override int Interpret(CurrentPricesContext context)
    {
        var totalSum = 0;
        foreach (var goods in GoodsInside)
        {
            totalSum += goods.Interpret(context);
        }
        return totalSum;
    }
}
// Термінальний вираз (зразу повертає значення взявши із його із контексту)
class TV : Goods
{
    public override int Interpret(CurrentPricesContext context)
    {
        int price = context.GetPrice("TV");
        Console.WriteLine("TV: {0}", price);
        return price;
    }
}
// Інші термінальні вирази (Laptop, Bed)

Як можна побачити GoodsPackage знає про те, як себе подати, а саме, коли він просумує ціну речей всередині. У нашому прикладі мова дуже проста, тільки з одним правилом, але у інших мовах усе може бути набагато складніше. Для прикладу, у якійсь уявній мові, пов'язаній із обрахунками, нетермінальними виразами зможуть бути звичайні “+”, “-”, “/”, “*”, “Sqrt”, “Integral”, або ще щось інше. Така мова також може мати ширший вибір термінальних виразів.

Залишилися ще дві речі, які відіграють помітну роль у патерні. Цей контекст (context) зберігає глобальну інформацію для процесу інтерпретування. У нашому прикладі контекст сьогоднішніх цін у певному місті дозволить порахувати сукупну ціну товарів. Ще однією роллю у патерні є клієнт, який відповідає за прочитання речення та виклик методу інтерпретації. Нижче наводиться тільки код клієнту, оскільки код контексту не є дуже важливим.

Уривок коду 15.1. Інтерпретер у дії

 class InterpreterDemo
{
    public static void Run()
    {
        new InterpreterDemo().RunInterpreterDemo();
    }

    public void RunInterpreterDemo()
    {
        // Дістаємо синтаксичне дерево, що представляє речення
        var truckWithGoods = PrepareTruckWithGoods();
        // Отримуємо останній контекст цін
        var pricesContext = GetRecentPricesContext();
        // Інтерпретуємо
        var totalPriceForGoods = truckWithGoods.Interpret(pricesContext);

        Console.WriteLine("Total: {0}", totalPriceForGoods);
    }

    private CurrentPricesContext GetRecentPricesContext()
    {
        var pricesContext = new CurrentPricesContext();
        pricesContext.SetPrice("Bed", 400);
        pricesContext.SetPrice("TV", 100);
        pricesContext.SetPrice("Laptop", 500);
        return pricesContext;
    }

    public GoodsPackage PrepareTruckWithGoods()
    {
        var truck = new GoodsPackage() { GoodsInside = new List<Goods>() };

        var bed = new Bed();
        var doubleTriplePackedBed = new GoodsPackage()
            { 
                GoodsInside = new List<Goods>() { new GoodsPackage() { 
                                    GoodsInside = new List<Goods>() { bed } } }
            };
        truck.GoodsInside.Add(doubleTriplePackedBed);
        truck.GoodsInside.Add(new TV());
        truck.GoodsInside.Add(new TV());
        truck.GoodsInside.Add(new GoodsPackage()
            { 
                GoodsInside = new List<Goods>() { 
                                     new Laptop(), new Laptop(), new Laptop() }
            });

        return truck;
    }
}

А ось вивід:

 Bed: 400
TV: 100
TV: 100
Laptop: 500
Laptop: 500
Laptop: 500
Total: 2100

І ще одне досить важливе: Інтерпретер — це такий дизайн патерн, який, швидше за все, вам ніколи не пригодиться у житті завдяки своєму дещо специфічному застосуванню.

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

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