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

13. Ланцюжок Відповідальностей — Chain of responsibility

13.	Ланцюжок Відповідальностей — Chain of responsibility

Уявіть, що ви пішли із своїми друзями в кафе. Кафе дещо специфічне — мало місця, і коли вам приносять якусь страву, зазвичай доводиться передавати її наступній людині за столом. Ваш найкращий друг сів найближче до краю, тому першим він і отримує до рук замовлення. Так як він мало спав зранку і любить поїсти м’ясця, то він ніколи не передесть вам м’ясної страви чи кави, допоки сам не насититься. Наступним після друга сидите ви, а далі ваша подружка, яка знаходиться біля стіни. Вона отримає все останньою, але, на щастя, вона хоче тільки капучіно, та й передавати їй уже не треба нікому.

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

Я думаю що весь механізм патерну Ланцюжка Відповідальностей є зрозумілим — ми маємо набір обробників (handlers) або відвідувачів кафе, які вміють обробляти команду (command) — їжу у нашому випадку. Якщо обробити команду не вдається, то вона передається наступному обробітнику.

Для прикладу із нашим кафе, загальним інтерфейсом відвідувача такого дивного кафе може бути такий базовий клас:

Уривок коду 13.1. Дивний відвідувач кафе (обробник)

 abstract class WierdCafeVisitor
{
    public WierdCafeVisitor CafeVisitor { get; private set; }
    protected WierdCafeVisitor(WierdCafeVisitor cafeVisitor)
    {
        CafeVisitor = cafeVisitor;
    }
    public virtual void HandleFood(Food food)
    {
        // Якщо не в змозі подужати їжу, передаємо її ближчому другові
        if (CafeVisitor != null)
        {
            CafeVisitor.HandleFood(food);
        }
    }
}

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

Уривок коду 13.2. Конкретний дивний відвідувач кафе — ваш кращий друг

 class  BestFriend : WierdCafeVisitor
{
    public List<Food> CoffeeContainingFood { get; private set; }
    public BestFriend(WierdCafeVisitor cafeVisitor) : base(cafeVisitor)
    {
        CoffeeContainingFood = new List<Food>();
    }

    public override void HandleFood(Food food)
    {
        if(food.Ingradients.Contains("Meat"))
        {
            Console.WriteLine(
              "BestFriend: I just ate {0}. It was tasty.",
              food.Name);
            return;
        }
        if (food.Ingradients.Contains("Coffee") && CoffeeContainingFood.Count < 1)
        {
            CoffeeContainingFood.Add(food);
            Console.WriteLine(
              "BestFriend: I have to take something with coffee. {0} looks fine.",  
              food.Name);
            return;
        }
        base.HandleFood(food);
    }
}

Реалізації ще двох обробітників — Me та GirlFriend мають бути зрозумілими, але все ж таки наведемо реалізацію відвідувача-подружки:

Уривок коду 13.3. Ваша подружка

 class  GirlFriend : WierdCafeVisitor
{
    public GirlFriend(WierdCafeVisitor cafeVisitor) : base(cafeVisitor)
    {
    }

    public override void HandleFood(Food food)
    {
        if(food.Name == "Cappuccino")
        {
            Console.WriteLine("GirlFriend: My lovely cappuccino!!!");
            return;
        }
        // Базовий виклик base.HandleFood(food) для останнього обробітника-дівчини 
        // не має сенсу, тому можна викинути ексепшин або нічого не робити
    }
}

Все відносно просто — дівчина хоче капучіно, але вона у ланцюжку остання, тому допоки ваш друг, який перший у ланцюжку, не вип’є щось із кофеїном, капучіно вона не отримає.
А тепер глянемо на все у дії. Створимо два капучіно, два супи (один м’ясний) і кусок м’яса, створимо наших відвідувачів кафе та будемо подавати їжу в руки другові:

Уривок коду 13.3. Замовляємо і передаємо

 var cappuccino1 = new Food("Cappuccino", new List<string> {"Coffee", "Milk",
                                                                     "Sugar"});
var cappuccino2 = new Food("Cappuccino", new List<string> {"Coffee", "Milk"});
var soup1 = new Food("Soup with meat", new List<string> {"Meat", "Water",
                                                                    "Potato"});
var soup2 = new Food("Soup with potato", new List<string> {"Water", "Potato"});
var meat = new Food("Meat", new List<string> {"Meat"});

var girlFriend = new GirlFriend(null);
var me = new Me(girlFriend);
var bestFriend = new BestFriend(me);

bestFriend.HandleFood(cappuccino1);
bestFriend.HandleFood(cappuccino2);
bestFriend.HandleFood(soup1);
bestFriend.HandleFood(soup2);
bestFriend.HandleFood(meat);

Вивід:

 BestFriend: I have to take something with coffee. Cappuccino looks fine.
GirlFriend: My lovely cappuccino!!!
BestFriend: I just ate Soup with meat. It was testy.
Me: I like Soup. It went well.
BestFriend: I just ate Meat. It was testy.

Як видно із виводу в консоль, дівчина отримала тільки друге капучіно, а ви були змушені їсти суп без м’яса :)

Що цікаво, мо ми можемо після моєї дівчини підчепити ще один обробітник — скажімо мішечок для собачки. Туди скидатимемо те, що ніхто не захоче їсти. Для цього прийдеться трішки змінити клас, щоб він мав метод на подобі SetNextCafeVisitor для динамічної додачі обробників.

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

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