Цепочка обязанностей (Chain of Responsibility)

Материал из Вики ИТ мехмата ЮФУ
Перейти к: навигация, поиск

К основной странице курса

Назначение

Некий запрос должен быть обработан в цепочке взаимосвязанных объектов (список, дерево - движение от листьев к корню). Объект либо обрабатывает запрос, либо передвает по цепочке следующему объекту. Если ни один из объектов не обработал запрос, то может происходить какое-то действие.

Описание

Имеется цепочка объектов, каждый хранит ссылку на следующий объект (как правило, объект более верхнего уровня) Запрос перемещается по цепочке объектов вверх пока один из них не обработает (Handle) этот запрос. У объекта, отправившего запрос на обработку, отсутствует информация о том, какой объект в цепочке обработает запрос.

Пример: докладная пишется непосредственному начальнику, он либо реагирует на нее, либо передает по иерархии вверх своему непосредственному начальнику и т.д.

Данный паттерн повсеместно используется в системах, реализующих графический интерфейс пользователя: нажатие на кнопку может быть обработано самой кнопкой, а может быть передано выше - компоненту, который владеет кнопкой (например, форме).

Использование

Паттерн Цепочка обязанностей используется если есть более одного объекта, способного обработать запрос, причем, настоящий обработчик неизвестен и должен быть найден автоматически

Реализация

Диаграмма классов

ChainCommon.png

Участники

  • Handler - обработчик

Определяет интерфейс для обработки запросов

  • ConcreteHandler - конкретный обработчик

Обрабатывает запрос если может

Имеет доступ к своему преемнику (successor)

Если не может обработать запрос, то переадресовывает его своему преемнику

  • Client - клиент

Направляет запрос некоторому объекту в цепочке

Пример

class MainApp
{
    static void Main()
    {
        // Setup Chain of Responsibility
        Handler h1 = new ConcreteHandler1();
        Handler h2 = new ConcreteHandler2();
        Handler h3 = new ConcreteHandler3();
        h1.SetSuccessor(h2);
        h2.SetSuccessor(h3);

        // Generate and process request
        int[] requests = { 2, 5, 14, 22, 18, 3, 27, 20 };

        h1.HandleRequest(2);
        h1.HandleRequest(22);
        h1.HandleRequest(222);
    }
}

abstract class Handler
{
    protected Handler successor;

    public void SetSuccessor(Handler successor)
    {
        this.successor = successor;
    }

    public abstract void HandleRequest(int request);
}

class ConcreteHandler1 : Handler
{
    public override void HandleRequest(int request)
    {
        if (request >= 0 && request < 10)
        {
            Console.WriteLine("{0} handled request {1}", this.GetType().Name, request);
        }
        else if (successor != null)
        {
            successor.HandleRequest(request);
        }
    }
}

class ConcreteHandler2 : Handler
{
    public override void HandleRequest(int request)
    {
        if (request >= 10 && request < 20)
        {
            Console.WriteLine("{0} handled request {1}", this.GetType().Name, request);
        }
        else if (successor != null)
        {
            successor.HandleRequest(request);
        }
    }
}

class ConcreteHandler3 : Handler
{
    public override void HandleRequest(int request)
    {
        if (request >= 20 && request < 30)
        {
            Console.WriteLine("{0} handled request {1}",this.GetType().Name, request);
        }
        else if (successor != null)
        {
            successor.HandleRequest(request);
        }
    }
}

Достоинства и недостатки

  • Паттерн Цепочка обязанностей освобождает объект от ответственности знать, кто обработает его запрос
  • Объекты, обрабатывающие запрос, можно включать в цепочку динамически
  • Недостаток: нет гарантий, что запрос вообще будет обработан

Варианты

  • Паттерн можно реализовать, используя Multicast делегаты .NET и добавляя/отсоединяя обработчики с помощью операций +=, -=
  • Существуют различные стратегии обработки
    • Обработчик по умолчанию препятствует пропаданию сообщений
    • Обработчики следующего уровня добавляют свою функциональность к функциональности базового обработчика
    • В обработчиках можно модифицировать схему передачи сообщений
  • Существуют различные стратегии пересылки
    • Обрабатываются все сообщения кроме тех, пересылка которых определена явным образом
    • Пересылаются все сообщения кроме тех, обработка которых определена явным образом
    • Любое сообщение, для которого явно не указано, должно и оно быть обработано или переслано, пересылается обработчику по умолчанию
    • Любое сообщение, для которого явно не указано, должно и оно быть обработано или переслано, отбрасывается
  • В цепочке можно обрабатывать запросы различных типов. Код запроса может передаваться обработчику, который должен содержать выбор действия в зависимости от кода запроса.
  • Можно использовать объекты-запросы, в которых инкапсулированы параметры запроса.
  • Класс Handler может предоставлять обработчики для некоторых типов запросов явно, а остальные определять в подклассах.