
Ланцюжок обов'язків на C#
Ланцюжок обов’язків — це поведінковий патерн, що дозволяє передавати запит ланцюжком потенційних обробників до тих пір, поки один з них не обробить його.
Позбавляє від жорсткої прив’язки відправника запиту до одержувача, дозволяючи динамічно вибудовувати ланцюг з різних обробників.
Складність:
Популярність:
Застосування: Патерн зустрічається в C# не так вже й часто, оскільки для його застосування потрібен ланцюг об’єктів, наприклад, зв’язаний список.
Ознаки застосування патерна: Ланцюжок обов’язків можна визначити за списками обробників або перевірок, через які пропускаються запити. Особливо, якщо порядок проходження обробників є важливим.
Концептуальний приклад
Цей приклад показує структуру патерна Ланцюжок обов’язків, а саме — з яких класів він складається, які ролі ці класи виконують і як вони взаємодіють один з одним.
Program.cs: Приклад структури патерна
using System; using System.Collections.Generic; namespace RefactoringGuru.DesignPatterns.ChainOfResponsibility.Conceptual { // The Handler interface declares a method for building the chain of // handlers. It also declares a method for executing a request. public interface IHandler { IHandler SetNext(IHandler handler); object Handle(object request); } // The default chaining behavior can be implemented inside a base handler // class. abstract class AbstractHandler : IHandler { private IHandler _nextHandler; public IHandler SetNext(IHandler handler) { this._nextHandler = handler; // Returning a handler from here will let us link handlers in a // convenient way like this: // monkey.SetNext(squirrel).SetNext(dog); return handler; } public virtual object Handle(object request) { if (this._nextHandler != null) { return this._nextHandler.Handle(request); } else { return null; } } } class MonkeyHandler : AbstractHandler { public override object Handle(object request) { if ((request as string) == "Banana") { return $"Monkey: I'll eat the {request.ToString()}.\n"; } else { return base.Handle(request); } } } class SquirrelHandler : AbstractHandler { public override object Handle(object request) { if (request.ToString() == "Nut") { return $"Squirrel: I'll eat the {request.ToString()}.\n"; } else { return base.Handle(request); } } } class DogHandler : AbstractHandler { public override object Handle(object request) { if (request.ToString() == "MeatBall") { return $"Dog: I'll eat the {request.ToString()}.\n"; } else { return base.Handle(request); } } } class Client { // The client code is usually suited to work with a single handler. In // most cases, it is not even aware that the handler is part of a chain. public static void ClientCode(AbstractHandler handler) { foreach (var food in new List<string> { "Nut", "Banana", "Cup of coffee" }) { Console.WriteLine($"Client: Who wants a {food}?"); var result = handler.Handle(food); if (result != null) { Console.Write($" {result}"); } else { Console.WriteLine($" {food} was left untouched."); } } } } class Program { static void Main(string[] args) { // The other part of the client code constructs the actual chain. var monkey = new MonkeyHandler(); var squirrel = new SquirrelHandler(); var dog = new DogHandler(); monkey.SetNext(squirrel).SetNext(dog); // The client should be able to send a request to any handler, not // just the first one in the chain. Console.WriteLine("Chain: Monkey > Squirrel > Dog\n"); Client.ClientCode(monkey); Console.WriteLine(); Console.WriteLine("Subchain: Squirrel > Dog\n"); Client.ClientCode(squirrel); } } }
Output.txt: Результат виконання
Chain: Monkey > Squirrel > Dog Client: Who wants a Nut? Squirrel: I'll eat the Nut. Client: Who wants a Banana? Monkey: I'll eat the Banana. Client: Who wants a Cup of coffee? Cup of coffee was left untouched. Subchain: Squirrel > Dog Client: Who wants a Nut? Squirrel: I'll eat the Nut. Client: Who wants a Banana? Banana was left untouched. Client: Who wants a Cup of coffee? Cup of coffee was left untouched.