
Цепочка обязанностей на C#
Цепочка обязанностей — это поведенческий паттерн, позволяющий передавать запрос по цепочке потенциальных обработчиков, пока один из них не обработает запрос.
Избавляет от жёсткой привязки отправителя запроса к его получателю, позволяя выстраивать цепь из различных обработчиков динамически.
Сложность:
Популярность:
Применимость: Паттерн встречается в C# не так уж часто, так как для его применения нужна цепь объектов, например, связанный список.
Признаки применения паттерна: Цепочку обязанностей можно определить по спискам обработчиков или проверок, через которые пропускаются запросы. Особенно если порядок следования обработчиков важен.
Концептуальный пример
Этот пример показывает структуру паттерна Цепочка обязанностей, а именно — из каких классов он состоит, какие роли эти классы выполняют и как они взаимодействуют друг с другом.
Program.cs: Пример структуры паттерна
using System; using System.Collections.Generic; namespace RefactoringGuru.DesignPatterns.ChainOfResponsibility.Conceptual { // Интерфейс Обработчика объявляет метод построения цепочки обработчиков. Он // также объявляет метод для выполнения запроса. public interface IHandler { IHandler SetNext(IHandler handler); object Handle(object request); } // Поведение цепочки по умолчанию может быть реализовано внутри базового // класса обработчика. abstract class AbstractHandler : IHandler { private IHandler _nextHandler; public IHandler SetNext(IHandler handler) { this._nextHandler = handler; // Возврат обработчика отсюда позволит связать обработчики простым // способом, вот так: // 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 { // Обычно клиентский код приспособлен для работы с единственным // обработчиком. В большинстве случаев клиенту даже неизвестно, что этот // обработчик является частью цепочки. 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) { // Другая часть клиентского кода создает саму цепочку. var monkey = new MonkeyHandler(); var squirrel = new SquirrelHandler(); var dog = new DogHandler(); monkey.SetNext(squirrel).SetNext(dog); // Клиент должен иметь возможность отправлять запрос любому // обработчику, а не только первому в цепочке. 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.