Цепочка обязанностей
(перенаправлено с «Chain of Responsibility»)
Цепочка обязанностей | |
---|---|
Chain of responsibility | |
Тип | поведенческий |
Назначение | для организации в системе уровней ответственности |
Родственные шаблоны | Компоновщик |
Описан в Design Patterns | Да |
Цепочка обязанностей (англ. Chain of responsibility) — поведенческий шаблон проектирования, предназначенный для организации в системе уровней ответственности.
Применение
Шаблон рекомендован для использования в условиях:
- в разрабатываемой системе имеется группа объектов, которые могут обрабатывать сообщения определенного типа;
- все сообщения должны быть обработаны хотя бы одним объектом системы;
- сообщения в системе обрабатываются по схеме «обработай сам либо перешли другому», то есть одни сообщения обрабатываются на том уровне, где они получены, а другие пересылаются объектам иного уровня.
Примеры
Пример на Swift
Исходный текст на Swift
class Error {
var statusCode = 500
}
protocol HandleErrorProtocol {
var statusCodes: ClosedRange<Int> { get }
var nextError: HandleErrorProtocol? { get set }
func getStatusCode(statusCode: Int)
}
class SuccessRequest: HandleErrorProtocol {
var nextError: HandleErrorProtocol? = ClientErrors()
var statusCodes = 200...299
func getStatusCode(statusCode: Int) {
statusCodes.contains(statusCode)
? print("Handle good request")
: nextError?.getStatusCode(statusCode: statusCode)
}
}
class ClientErrors: HandleErrorProtocol {
var nextError: HandleErrorProtocol? = ServerErrors()
var statusCodes = 400...499
func getStatusCode(statusCode: Int) {
statusCodes.contains(statusCode)
? print("Handle client error")
: nextError?.getStatusCode(statusCode: statusCode)
}
}
class ServerErrors: HandleErrorProtocol {
var nextError: HandleErrorProtocol?
var statusCodes = 500...599
func getStatusCode(statusCode: Int) {
statusCodes.contains(statusCode)
? print("Handle server error")
: print("Cannot identify error")
}
}
let error = Error()
let request = SuccessRequest()
request.getStatusCode(statusCode: error.statusCode)
Пример на Delphi
Исходный текст на Delphi
unit Pattern;
interface
uses SysUtils;
type
TPurchase = class
public
Number: integer;
Amount: Double;
Purpose: string;
constructor Create(num: integer; am: Double; pur: string);
end;
IApprover = interface
['{3ACA3967-FFCF-48A1-AC45-9A9B98A8DD96}']
procedure SetSuccessor(successor: IApprover);
procedure ProcessRequest(purchase: TPurchase);
end;
TApprover = class(TInterfacedObject, IApprover)
protected
FSuccessor: IApprover;
public
procedure SetSuccessor(successor: IApprover);
procedure ProcessRequest(purchase: TPurchase); virtual; abstract;
end;
TDirector = class(TApprover)
procedure ProcessRequest(purchase: TPurchase); override;
end;
TVicePresident = class(TApprover)
procedure ProcessRequest(purchase: TPurchase); override;
end;
TPresident = class(TApprover)
procedure ProcessRequest(purchase: TPurchase); override;
end;
implementation
{ TApprover }
procedure TApprover.SetSuccessor(successor: IApprover);
begin
FSuccessor := successor;
end;
{ TDirector }
procedure TDirector.ProcessRequest(purchase: TPurchase);
begin
if purchase.Amount < 10000.0 then
WriteLn(Format('Director approved request # %d', [purchase.Number]))
else if FSuccessor <> nil then
FSuccessor.ProcessRequest(purchase);
end;
{ TVicePresident }
procedure TVicePresident.ProcessRequest(purchase: TPurchase);
begin
if purchase.Amount < 25000.0 then
WriteLn(Format('VicePresident approved request # %d', [purchase.Number]))
else if FSuccessor <> nil then
FSuccessor.ProcessRequest(purchase);
end;
{ TPresident }
procedure TPresident.ProcessRequest(purchase: TPurchase);
begin
if purchase.Amount < 100000.0 then
WriteLn(Format('President approved request # %d', [purchase.Number]))
else
WriteLn(Format('Request# %d requires an executive meeting!', [purchase.Number]))
end;
{ TPurchase }
constructor TPurchase.Create(num: integer; am: Double; pur: string);
begin
Number := num;
Amount := am;
Purpose := pur;
end;
end.
//______________________________________________________________________
program Behavioral.ChainOfResponsibility.Pattern;
{$APPTYPE CONSOLE}
uses
SysUtils,
Pattern in 'Pattern.pas';
var
Director: IApprover;
VicePresident: IApprover;
President: IApprover;
Purchase: TPurchase;
begin
ReportMemoryLeaksOnShutDown := DebugHook <> 0;
try
Director := TDirector.Create;
VicePresident := TVicePresident.Create;
President := TPresident.Create;
try
Director.SetSuccessor(VicePresident);
VicePresident.SetSuccessor(President);
Purchase := TPurchase.Create(2034, 350.00, 'Supplies');
Director.ProcessRequest(Purchase);
Purchase.Free;
Purchase := TPurchase.Create(2035, 32590.10, 'Project X');
Director.ProcessRequest(Purchase);
Purchase.Free;
Purchase := TPurchase.Create(2036, 122100.00, 'Project Y');
Director.ProcessRequest(Purchase);
ReadLn;
finally
Purchase.Free;
end;
except
on E:Exception do
Writeln(E.Classname, ': ', E.Message);
end;
end.
Пример на PHP 5
Исходный текст на PHP 5.3
namespace ChainOfResponsibility {
abstract class Logger {
const ERR = 3;
const NOTICE = 5;
const DEBUG = 7;
protected $mask;
// Следующий элемент в цепочке обязанностей
protected $next;
public function __construct($mask) {
$this->mask = $mask;
}
public function setNext(Logger $log) {
$this->next = $log;
return $log;
}
public function message($msg, $priority) {
if ($priority <= $this->mask) {
$this->writeMessage($msg);
}
if ($this->next != null) {
$this->next->message($msg, $priority);
}
}
protected abstract function writeMessage($msg);
}
class StdoutLogger extends Logger {
protected function writeMessage($msg) {
echo sprintf("Writing to stdout:%s\n", $msg);
}
}
class EmailLogger extends Logger {
protected function writeMessage($msg) {
echo sprintf("Sending via email:%s\n", $msg);
}
}
class StderrLogger extends Logger {
protected function writeMessage($msg) {
echo sprintf("Sending to stderr:%s\n", $msg);
}
}
//цепочка обязанностей
class ChainOfResponsibilityExample {
public function run() {
// строим цепочку обязанностей
$logger = new StdoutLogger(Logger::DEBUG);
$logger1 = $logger->setNext(new EmailLogger(Logger::NOTICE));
$logger2 = $logger1->setNext(new StderrLogger(Logger::ERR));
// Handled by StdoutLogger
$logger->message("Entering function y.", Logger::DEBUG);
// Handled by StdoutLogger and EmailLogger
$logger->message("Step1 completed.", Logger::NOTICE);
// Handled by all three loggers
$logger->message("An error has occurred.", Logger::ERR);
}
}
$chain = new ChainOfResponsibilityExample();
$chain->run();
}
Пример на Java
Исходный текст на Java
package chainofresp;
abstract class Logger {
public static int ERR = 3;
public static int NOTICE = 5;
public static int DEBUG = 7;
protected int mask;
// The next element in the chain of responsibility
protected Logger next;
public Logger setNext(Logger log) {
next = log;
return log;
}
public void message(String msg, int priority) {
if (priority <= mask) {
writeMessage(msg);
}
if (next != null) {
next.message(msg, priority);
}
}
abstract protected void writeMessage(String msg);
}
class StdoutLogger extends Logger {
public StdoutLogger(int mask) {
this.mask = mask;
}
protected void writeMessage(String msg) {
System.out.println("Writing to stdout: " + msg);
}
}
class EmailLogger extends Logger {
public EmailLogger(int mask) {
this.mask = mask;
}
protected void writeMessage(String msg) {
System.out.println("Sending via email: " + msg);
}
}
class StderrLogger extends Logger {
public StderrLogger(int mask) {
this.mask = mask;
}
protected void writeMessage(String msg) {
System.out.println("Sending to stderr: " + msg);
}
}
public class ChainOfResponsibilityExample {
public static void main(String[] args) {
// Build the chain of responsibility
Logger logger, logger1,logger2;
logger = new StdoutLogger(Logger.DEBUG);
logger1 = logger.setNext(new EmailLogger(Logger.NOTICE));
logger2 = logger1.setNext(new StderrLogger(Logger.ERR));
// Handled by StdoutLogger
logger.message("Entering function y.", Logger.DEBUG);
// Handled by StdoutLogger and EmailLogger
logger.message("Step1 completed.", Logger.NOTICE);
// Handled by all three loggers
logger.message("An error has occurred.", Logger.ERR);
}
}
/*
The output is:
Writing to stdout: Entering function y.
Writing to stdout: Step1 completed.
Sending via e-mail: Step1 completed.
Writing to stdout: An error has occurred.
Sending via e-mail: An error has occurred.
Sending to stderr: An error has occurred.
*/
Пример на C#
Исходный текст на языке C#
// Chain of Responsibility pattern -- Structural example
using System;
namespace DoFactory.GangOfFour.Chain.Structural
{
/// <summary>
/// MainApp startup class for Structural
/// Chain of Responsibility Design Pattern.
/// </summary>
class MainApp
{
/// <summary>
/// Entry point into console application.
/// </summary>
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 };
foreach (int request in requests)
{
h1.HandleRequest(request);
}
// Wait for user
Console.ReadKey();
}
}
/// <summary>
/// The 'Handler' abstract class
/// </summary>
abstract class Handler
{
protected Handler successor;
public void SetSuccessor(Handler successor)
{
this.successor = successor;
}
public abstract void HandleRequest(int request);
}
/// <summary>
/// The 'ConcreteHandler1' class
/// </summary>
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);
}
}
}
/// <summary>
/// The 'ConcreteHandler2' class
/// </summary>
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);
}
}
}
/// <summary>
/// The 'ConcreteHandler3' class
/// </summary>
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);
}
}
}
}
Output
ConcreteHandler1 handled request 2
ConcreteHandler1 handled request 5
ConcreteHandler2 handled request 14
ConcreteHandler3 handled request 22
ConcreteHandler2 handled request 18
ConcreteHandler1 handled request 3
ConcreteHandler3 handled request 27
ConcreteHandler3 handled request 20
Пример на C++
Исходный текст на языке C++
#include <iostream>
/**
* Вспомогательный класс, описывающий некоторое преступление
*/
class CriminalAction {
friend class Policeman; // Полицейские имеют доступ к материалам следствия
int complexity; // Сложность дела
const char* description; // Краткое описание преступления
public:
CriminalAction(int complexity, const char* description): complexity(complexity), description(description) {}
};
/**
* Абстрактный полицейский, который может заниматься расследованием преступлений
*/
class Policeman {
protected:
int deduction; // дедукция (умение распутывать сложные дела) у данного полицейского
Policeman* next; // более умелый полицейский, который получит дело, если для текущего оно слишком сложное
virtual void investigateConcrete(const char* description) {} // собственно расследование
public:
Policeman(int deduction) : deduction(deduction), next(nullptr) {}
virtual ~Policeman() {
delete next;
}
/**
* Добавляет в цепочку ответственности более опытного полицейского, который сможет принять на себя
* расследование, если текущий не справится
*/
Policeman* setNext(Policeman* policeman) {
next = policeman;
return next;
}
/**
* Полицейский начинает расследование или, если дело слишком сложное, передает его более опытному коллеге
*/
void investigate(CriminalAction* criminalAction) {
if (deduction < criminalAction->complexity) {
if (next) {
next->investigate(criminalAction);
} else {
std::cout << "Это дело не раскрыть никому." << std::endl;
}
} else {
investigateConcrete(criminalAction->description);
}
}
};
class MartinRiggs: public Policeman {
protected:
void investigateConcrete(const char* description) {
std::cout << "Расследование по делу \"" << description << "\" ведет сержант Мартин Риггс" << std::endl;
}
public:
MartinRiggs(int deduction): Policeman(deduction) {}
};
class JohnMcClane: public Policeman {
protected:
void investigateConcrete(const char* description) {
std::cout << "Расследование по делу \"" << description << "\" ведет детектив Джон Макклейн" << std::endl;
}
public:
JohnMcClane(int deduction): Policeman(deduction) {}
};
class VincentHanna: public Policeman {
protected:
void investigateConcrete(const char* description) {
std::cout << "Расследование по делу \"" << description << "\" ведет лейтенант Винсент Ханна" << std::endl;
}
public:
VincentHanna(int deduction): Policeman(deduction) {}
};
int main() {
std::cout << "OUTPUT:" << std::endl;
Policeman* policeman = new MartinRiggs(3); // полицейский с наименьшим навыком ведения расследований
policeman
->setNext(new JohnMcClane(5))
->setNext(new VincentHanna(8)); // добавляем ему двух опытных коллег
policeman->investigate(new CriminalAction(2, "Торговля наркотиками из Вьетнама"));
policeman->investigate(new CriminalAction(7, "Дерзкое ограбление банка в центре Лос-Анджелеса"));
policeman->investigate(new CriminalAction(5, "Серия взрывов в центре Нью-Йорка"));
return 0;
}
/**
* OUTPUT:
* Расследование по делу "Торговля наркотиками из Вьетнама" ведет сержант Мартин Риггс
* Расследование по делу "Дерзкое ограбление банка в центре Лос-Анджелеса" ведет лейтенант Винсент Ханна
* Расследование по делу "Серия взрывов в центре Нью-Йорка" ведет детектив Джон Макклейн
*/
Пример на Python
Исходный текст на языке Python
handlers = []
def car_handler(func):
handlers.append(func)
return func
class Car:
def __init__(self):
self.name = None
self.km = 11100
self.fuel = 5
self.oil = 5
@car_handler
def handle_fuel(car):
if car.fuel < 10:
print("added fuel")
car.fuel = 100
@car_handler
def handle_km(car):
if car.km > 10000:
print("made a car test.")
car.km = 0
@car_handler
def handle_oil(car):
if car.oil < 10:
print("Added oil")
car.oil = 100
class Garage:
def __init__(self, handlers=None):
self.handlers = handlers or []
def add_handler(self, handler):
self.handlers.append(handler)
def handle_car(self, car):
for handler in self.handlers:
handler(car)
if __name__ == '__main__':
garage = Garage(handlers)
car = Car()
garage.handle_car(car)
Ссылки
- Паттерн Chain of Responsibility (цепочка обязанностей) — назначение, описание, особенности и реализация на C++.
Примечания