一、职责链(Chain of Responsibility,简称“CoR”)
1、引言
职责链有几种典型的应用:
(1)在MFC中,消息(如WM_COMMAND)是通过一个向上递交的链式处理策略对用户事件(如点击鼠标)进行处理的。
(2)击鼓传花是一种热闹而又紧张的饮酒游戏。在酒宴上宾客依次坐定位置,由一人击鼓,击鼓的地方与传花的地方是分开的,以示公正。开始击鼓时,花束就开始依次传递,鼓声一落,如果花束在某人手中,则该人就得饮酒。击鼓传花便是责任链模式的应用。参见:http://www.cnblogs.com/zhenyulu/articles/65850.html
2、一般思路
下图Chain of Responsibility模式中,抽象处理者Handler定义了一个处理请求的接口。如果需要,接口可以定义出一个方法,以设定和返回对下家的引用。而具体处理者ConcreteHandler将自己的后继对象(向下传递消息的对象)记录在后继表HandlerSuccessor中,当一个请求到来时,先检查看自己有没有匹配的处理程序,如果有就自己处理,否则传递给它的后继。
Chain of Responsibility模式的最大的一个优点就是降低了请求的发送端和接收端之间的耦合性,请求的发送者完全不必知道该请求会被哪个应答对象处理,链上的多个对象都有机会处理这个请求。一个链可以是一条线,一个树,也可以是一个环。
3、典型代码
// ChainOfResposibility.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <vector>
#include <iostream>
#include <conio.h>
#include <string>
using namespace std;
class Handler
{
protected:
Handler *_successor;
public:
Handler(){_successor = 0;}
virtual ~Handler(){}
void SetSuccessor(Handler *successor)
{
this->_successor = successor;
}//
virtual void HandleRequest(int request) = 0;
};
class ConcreteHandler1 : public Handler
{
public:
ConcreteHandler1(){}
~ConcreteHandler1(){}
void HandleRequest(int request)
{
if( request >= 0 && request < 10 )
cout<<"{ConcreteHandler1} handled request {"<<request<<"}"<<endl;
else
if( _successor != 0 )
_successor->HandleRequest( request );
}
};
class ConcreteHandler2 : public Handler
{
public:
ConcreteHandler2(){}
~ConcreteHandler2(){}
void HandleRequest(int request)
{
if( request >= 10 && request < 20 )
cout<<"{ConcreteHandler2} handled request {"<<request<<"}"<<endl;
else
if( _successor != 0 )
_successor->HandleRequest( request );
}
};
class ConcreteHandler3 : public Handler
{
public:
ConcreteHandler3(){}
~ConcreteHandler3(){}
void HandleRequest(int request)
{
if( request >= 20 && request < 30 )
cout<<"{ConcreteHandler3} handled request {"<<request<<"}"<<endl;
else
if( _successor != 0 )
_successor->HandleRequest( request );
}
};
int _tmain(int argc, _TCHAR* argv[])
{
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 };
for(int i=0; i<8; i++)
{
h1->HandleRequest( requests[i] );
}
_getch();/*等待按键继续*/
return 0;
}
运行情况如下:
4、应用提示
(1)责任链模式并不创建责任链。责任链的创建必须由系统的其它部分创建出来(如用户代码main()中的h1->SetSuccessor(h2));或者与其它模式联合使用,比如利用Composite模式中一个构件的父构件作为它的后继。
(2)对于HandleRequest()请求参数request,可以使用独立的请求对象来进行封装,从而更加明确地描述请求。当然,请求对象的访问方式略微复杂些。
二、命令(Command,别名“动作/行动Action、事务/交易Transaction”)
1、引言
命令模式是对命令的封装。所谓命令是指这样一个操作:请求方发出一个操作请求,接收方执行操作。将命令封装为独立对象,可以将“行为请求者”与“行为实现者”解耦。另外,需要记录请求日志、对请求进行排队、支持撤销Undo操作等等功能时,可以使用该模式。
2、一般思路
命令模式有两种实现方式:Seperate和Combine方式。后者不需要Invoke请求者来参与,command定义关于操作的接口(前者中command只定义execute())。
下图为前者的典型类图。首先,客户(Client)需要创建一个具体命令(ConcreteCommand)对象并确定其接收者Receiver。然后,Invoker发送操作请求,由Command的Execute()传递给Receiver的Acion()执行。这样Invoker对象根本就不需要知道具体是哪个对象在处理excute操作(当然要知道是Command类的对象,仅此而已)。
3、实例分析
实例一:参考http://wenku.baidu.com/view/5af80463ddccda38376bafdb.html 车的运动
// Command1.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <vector>
#include <iostream>
#include <conio.h>
#include <string>
using namespace std;
class CarReceiver
{
public:
CarReceiver(){}
~CarReceiver(){}
void AcitonStart() {cout<< "启动..."<<endl;}
void AcitonMoveForwards() {cout<< "前进..."<<endl;}
void AcitonStop() {cout<< "停止..."<<endl;}
void AcitonMoveBackwards() {cout<< "后退..."<<endl;}
};
class CarCommand
{
public:
CarReceiver *_carReceiver;
CarCommand(CarReceiver *carReceiver){this->_carReceiver = carReceiver;}
virtual ~CarCommand(){}
virtual void Excecute() = 0;
};
class CarStartCommand : public CarCommand
{
public:
CarStartCommand(CarReceiver *carReceiver):CarCommand(carReceiver){}
~CarStartCommand(){}
void Excecute(){_carReceiver->AcitonStart();}
};
class CarMoveForwardsCommand : public CarCommand
{
public:
CarMoveForwardsCommand(CarReceiver *carReceiver):CarCommand(carReceiver){}
~CarMoveForwardsCommand(){}
void Excecute(){_carReceiver->AcitonMoveForwards();}
};
class CarMoveBackwardsCommand : public CarCommand
{
public:
CarMoveBackwardsCommand(CarReceiver *carReceiver):CarCommand(carReceiver){}
~CarMoveBackwardsCommand(){}
void Excecute(){_carReceiver->AcitonMoveBackwards();}
};
class CarStopCommand : public CarCommand
{
public:
CarStopCommand(CarReceiver *carReceiver):CarCommand(carReceiver){}
~CarStopCommand(){}
void Excecute(){_carReceiver->AcitonStop();}
};
class CarInvoker
{
public:
CarCommand* _start;
CarCommand* _stop;
CarCommand* _moveforwards;
CarCommand* _movebackwards;
CarInvoker(CarCommand* start,CarCommand* stop,CarCommand* moveforwards,CarCommand* movebackwards)
{
this->_start = start;
this->_stop = stop;
this->_moveforwards = moveforwards;
this->_movebackwards = movebackwards;
}
virtual ~CarInvoker(){}
void Start() {_start->Excecute();}
void Stop() {_stop->Excecute();}
void MoveForwards() {_moveforwards->Excecute();}
void MoveBackwards(){_movebackwards->Excecute();}
};
int _tmain(int argc, _TCHAR* argv[])
{
CarReceiver *mycar = new CarReceiver();
CarCommand *startcmd = new CarStartCommand(mycar);
CarCommand *stopcmd = new CarStopCommand(mycar);
CarCommand *moveforwardscmd = new CarMoveForwardsCommand(mycar);
CarCommand *movebackwardscmd = new CarMoveBackwardsCommand(mycar);
CarInvoker *carinvoker = new CarInvoker(startcmd,stopcmd,moveforwardscmd,movebackwardscmd);
while (1)
{
char usercmd;
cin>> usercmd;
switch(usercmd)
{
case 'a':
carinvoker->Start();
break;
case 'p':
carinvoker->Stop();
break;
case 'f':
carinvoker->MoveForwards();
break;
case 'b':
carinvoker->MoveBackwards();
break;
default:
break;
}
usercmd = ' ';
}
delete mycar;
delete startcmd;
delete stopcmd;
delete moveforwardscmd;
delete movebackwardscmd;
delete carinvoker;
// _getch();/*等待按键继续*/
return 0;
}
运行情况截图如下:
实例二:参考 http://www.cnblogs.com/zhenyulu/articles/69858.html简单的计算器,支持Undo撤销操作。
如果只需要提供一层的undo和redo,那么系统只需要存储最后被执行的那个命令对象。如果需要支持多层的undo和redo,那么系统就需要存储曾经被执行过的命令的清单,清单能允许的最大的长度便是系统所支持的undo和redo的层数。沿着清单逆着执行清单上的命令的反命令(unExecute())便是undo;沿着清单顺着执行清单上的命令便是redo。
// Command2.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <vector>
#include <iostream>
#include <conio.h>
#include <string>
#include <vector>
using namespace std;
// "Receiver"
class Calculator
{
public:
Calculator ()
{
total = 0;
cout<<"Total = 0"<<endl;
}
~Calculator (){}
private:
int total;
public:
void Operation( char _operator, int operand )
{
switch( _operator )
{
case '+': total += operand; break;
case '-': total -= operand; break;
case '*': total *= operand; break;
case '/': total /= operand; break;
default: break;
}
cout<<"Total = "<<total<<" ( following "<<_operator<<" "<<operand<<" )"<<endl;
}
};
// "Command"
class Command
{
public:
Command(){}
virtual ~Command(){}
virtual void Execute() = 0;
virtual void UnExecute() = 0;
};
// "ConcreteCommand"
class CalculatorCommand : Command
{
char _operator;
int operand;
Calculator *calculator;
public:
CalculatorCommand( Calculator *calculator,
char _operator, int operand )
{
this->calculator = calculator;
this->_operator = _operator;
this->operand = operand;
}
~CalculatorCommand(){}
public:
void Execute()
{
calculator->Operation( _operator, operand );
}
void UnExecute()
{
calculator->Operation( Undo( _operator ), operand );
}
private:
char Undo( char _operator )
{
char undo = ' ';
switch( _operator )
{
case '+': undo = '-'; break;
case '-': undo = '+'; break;
case '*': undo = '/'; break;
case '/': undo = '*'; break;
default: break;
}
return undo;
}
};
// "Invoker"
class User
{
public:
vector<CalculatorCommand> commands;
unsigned int current;
User(){ current = 0; }
virtual ~User() {}
// Methods
public:
void Redo( int levels )
{
cout<<"---- Redo "<<levels<<"levels ----"<<endl;
// Perform redo operations
for( int i = 0; i < levels; i++ )
if( current < (commands.size() - 1) )
commands[ current++ ].Execute();
}
void Undo( int levels )
{
cout<<"---- Undo "<<levels<<"levels ----"<<endl;
// Perform undo operations
for( int i = 0; i < levels; i++ )
if( current > 0 )
commands[ --current ].UnExecute();
}
void Compute(Calculator* calculator, char _operator, int operand )
{
// Create command operation and execute it
CalculatorCommand *command = new CalculatorCommand(calculator, _operator, operand);
command->Execute();
// Add command to undo list
commands.push_back(*command);
current++;
delete command;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
Calculator* calculator = new Calculator();
User* user = new User();
user->Compute( calculator,'+', 100 );
user->Compute( calculator,'-', 50 );
user->Compute( calculator,'*', 10 );
user->Compute( calculator,'/', 2 );
// Undo and then redo some commands
user->Undo( 4 );
user->Redo( 3 );
delete user;
_getch();/*等待按键继续*/
return 0;
}
运行情况截图如下:
4、应用提示
(1)通过使用Composite模式,可将多个“命名”封装为一个“复合命令”MacroCommand类。一般来说,复合命令是Composite模式的一个实例。宏命令便是复合命令的一个例子。
(2)命令模式的缺点在于,可能会导致系统有过多的具体命令类ConcreteCommand。某些系统可能需要几十,几百甚至几千个具体命令类,这会使命令模式在这样的系统里不适用。
(3)备忘录Memento模式可用来保持某个状态,命令模式使用该状态来取消它的效果,能有效避免取消操作过程中的错误积累。