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

Название и классификация паттерна

Цепочка обязанностей — паттерн поведения объектов.

Назначение

Паттерн Chain of Responsibility позволяет избежать жесткой зависимости отправителя запроса от его получателя, при этом запрос может быть обработан несколькими объектами. Объекты-получатели связываются в цепочку. Запрос передается по этой цепочке, пока не будет обработан.

Вводит конвейерную обработку для запроса с множеством возможных обработчиков.

Объектно-ориентированный связанный список с рекурсивным обходом.

Решаемая проблема

Имеется поток запросов и переменное число обработчиков этих запросов. Запрос может быть обработан различными объектами, но заранее неизвестно какими, и эти объекты должны быть найдены автоматически без явного указания набора объектов, способных обработать запрос (рис. 60).

Паттерн Цепочка обязанностей инкапсулирует элементы по обработке запросов внутри абстрактного «конвейера». Клиенты «кидают» свои запросы на вход этого конвейера (рис. 61).

Request

Схема потока запросов

Рис. 60. Схема потока запросов

Схема работы паттерна Цепочка обязанностей

Рис. 61. Схема работы паттерна Цепочка обязанностей

Паттерн Цепочка обязанностей связывает в цепочку объекты-получатели, а затем передает запрос-сообщение от одного объекта к другому до тех пор, пока не достигнет объекта, способного его обработать. Число и типы объектов-обработчиков заранее неизвестны, они могут настраиваться динамически. Механизм связывания в цепочку использует рекурсивную композицию, что позволяет использовать неограниченное число обработчиков.

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

Структура паттерна Цепочка обязанностей

Производные классы знают, как обрабатывать запросы клиентов. Если «текущий» объект не может обработать запрос, то он делегирует его базовому классу, который делегирует «следующему» объекту и т. д. (рис. 62).

UML-диаграмма паттерна Цепочка обязанностей

Рис. 62. UML-диаграмма паттерна Цепочка обязанностей

Участники

Handler — обработчик: определяет интерфейс для обработки запросов; (необязательно) реализует связь с преемником.

HandlerOne (HandlerTwo...) — конкретный обработчик: обрабатывает запрос, за который отвечает; имеет доступ к своему преемнику; если Handler способен обработать запрос, то так и делает, если не может, то направляет его своему преемнику.

Client — клиент: отправляет запрос некоторому объекту Handler в цепочке.

Отношения

Когда клиент инициирует запрос, он продвигается по цепочке, пока некоторый объект Handler не возьмет на себя ответственность за его обработку. Обработчики могут вносить свой вклад в обработку каждого запроса. Запрос может быть передан по всей длине цепочки до самого последнего звена.

Пример паттерна Цепочка обязанностей

Паттерн Chain of Responsibility позволяет избежать привязки отправителя запроса к его получателю, давая шанс обработать запрос нескольким получателям. Банкомат использует Chain of Responsibility в механизме выдачи денег.

Использование паттерна Цепочка обязанностей

Базовый класс имеет указатель на «следующий обработчик».

Каждый производный класс реализует свой вклад в обработку запроса.

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

Клиент (или третья сторона) создает цепочку получателей (которая может иметь ссылку с последнего узла на корневой узел).

Клиент передает каждый запрос в начало цепочки.

Рекурсивное делегирование создает иллюзию волшебства.

Особенности паттерна Chain of Responsibility

Паттерны Chain of Responsibility, Command, Mediator и Observer показывают, как можно разделить отправителей и получателей с учетом их особенностей. Chain of Responsibility передает запрос отправителя по цепочке потенциальных получателей.

Chain of Responsibility может использовать Command для представления запросов в виде объектов.

Chain of Responsibility часто применяется вместе с паттерном Composite. Родитель компонента может выступать в качестве его преемника.

Реализация паттерна Chain of Responsibility по шагам

Создайте указатель на следующий обработчик next в базовом классе.

Метод handle() базового класса всегда делегирует запрос следующему объекту.

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

#include

#include

#include using namespace std;

class Base

{

// 1. Указатель "next" в базовом классе Base *next; public:

Base()

{

next = 0;

}

void setNext(Base *n)

{

next = n;

}

void add(Base *n)

{

if (next) next->add(n); else

next = n;

}

// 2. Метод базового класса, делегирующий запрос next-объекту virtual void handle(int i)

{

next->handle(i);

}

};

class Handler 1: public Base

{

public:

/*virtual*/void handle(int i)

{

if (rand() % 3)

{

// 3. 3 из 4 запросов не обрабатываем co?t << "HI passsed " << i << "

// 3. и делегируем базовому классу Base::handle(i); } else

cout << "Н1 handled " « i « "

}

};

class Handler2: public Base

{

public:

/*virtual*/void handle(int i)

{

if (rand() % 3)

{

cout << "H2 passsed " << i << " Base::handle(i);

}

else

cout « "H2 handled " « i « "

}

};

class Handler3: public Base

{

public:

/*virtual*/void handle(int i)

{

if (rand() % 3)

{

cout << "H3 passsed " << i << " Base::handle(i);

}

else

cout « "H3 handled" « i « "

}

};

int main()

{

srand(time(0)); Handlerl root; Handler2 two; Handler3 thr; root.add(&two); root.add(&thr);

thr.setNext(&root); for (int i = 1 ; i < 10; i++)

{

root.handle(i); cout << ' ';

}

}

Вывод программы:

Hl passsed 1 H2 passsed 1 H3 passsed 1 Hl passsed 1 H2 handled 1 H1 handled 2 H1 handled 3

H1 passsed 4 H2 passsed 4 H3 handled 4 H1 passsed 5 H2 handled 5

H1 passsed 6 H2 passsed 6 H3 passsed 6 H1 handled 6

Hl passsed 7 H2 passsed 7 H3 passsed 7 Hl passsed 7 H2 handled 7

H1 handled 8

Hl passsed 9 H2 passsed 9 H3 handled 9

Родственные паттерны

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

 
< Пред   СОДЕРЖАНИЕ     След >