command 模式:
在客户和服务之间增加了一个中间层,使得双方相互独立。即,将命令的发送和接收分离。
而对服务端和客户段可见的只是中间层提供的接口。
command 模式就是面向接口的模式。
中间层将客户端欲调用的所有请求封装成类,客户端一改往日的直接调用命令为调用中间层命令类的对象,从而对客户端屏蔽了所有命令的细节。此法能有效降低耦合度(模块之间的联系程度)。当中间层命令代码改变时无需修改客户端代码。
对command模式uml图的分析:
command模式uml图分为两个部分:
外部视图:用一个参数化的协作来完成,参数说明了该协作实例化时应该实现的角色。
内部视图:用类图来表示结构,用顺序图来表示行为。
由顺序图得知command调用顺序为:
Client发送request给Invoker;
Invoker调用ConcreteCommand的Execute()方法;
Execute()方法调用Receiver的Action()(该Aciton为request的具体实现)。
所涉及的角色:
客户(Client)角色:创建了一个具体命令(ConcreteCommand)对象并确定其接收者。相当于客户端。
命令(Command)角色:声明了一个给所有具体命令类的抽象接口。这是一个抽象角色,(含有Execute()抽象方法) 。
具体命令(ConcreteCommand)角色:定义一个接受者和行为之间的弱耦合,是command接口的实现,实现Execute()方法,负责调用接收到的相应操作。Execute()方法通常叫做执方法。
请求者(Invoker)角色:负责调用命令对象执行请求,相关的方法叫做行动方法。
接收者(Receiver)角色:负责具体实施和执行一个请求。相当于服务端,任何一个类都可以成为接收者。实施和执行请求的方法叫做行动方法。
具体用到命令模式的例子有界面按钮等操作,ejb调用等。
对于J2EE:
client相当于用户浏览器,发送命令。
Invoker相当于servlet或jsp,它发送客户的请求。
command接口相当于remote接口
ConcreteCommand相当于EJB对象或EJB容器(EJB对象就属于EJB容器,它负责调用Bean的商务方法)。
Receiver相当于具体的企业Bean,用来实现命令请求的细节。
来看一个用command模式实现的计算器程序(C#实现):
1.command抽象类:
abstract class Command //此处用抽象类来完成接口的工作。
{
abstract public void Execute();
abstract public void UnExecute();
}
2.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;
}
// 属性
public char Operator
{
set{ @operator = value; }
}
public int Operand
{
set{ operand = value; }
}
// 覆写command的抽象方法,Execute()。此处的Execute()并没有真正执行命令细节,而是进一步调用receiver的方法,由receiver来执行命令的细节,但对客户端是屏蔽的
override public void Execute()
{
calculator.Operation( @operator, operand );
}
override public void UnExecute()
{
calculator.Operation( Undo( @operator ), operand );
}
// undo方法
private char Undo( char @operator )
{
char undo = ' ';
switch( @operator )
{
case '+': undo = '-'; break;
case '-': undo = '+'; break;
case '*': undo = '/'; break;
case '/': undo = '*'; break;
}
return undo;
}
}
3.Receiver:
class Calculator
{
// 变量
private int total = 0;
// 命令的具体实现,仅由command对象来调用,与客户端命令的调用完全独立
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;
}
Console.WriteLine( "Total = {0} (following {1} {2})",
total, @operator, operand );
}
}
4.Invoker
class User
{
// 变量
private Calculator calculator = new Calculator();
private ArrayList commands = new ArrayList();
private int current = 0;
// 调用者调用客户的请求
public void Redo( int levels )
{
Console.WriteLine( "---- Redo {0} levels ", levels );
// Perform redo operations
for( int i = 0; i < levels; i++ )
if( current < commands.Count - 1 )
((Command)commands[ current++ ]).Execute();
}
public void Undo( int levels )
{
Console.WriteLine( "---- Undo {0} levels ", levels );
// Perform undo operations
for( int i = 0; i < levels; i++ )
if( current > 0 )
((Command)commands[ --current ]).UnExecute();
}
public void Compute( char @operator, int operand )
{
// 将command对象封装入invoker的Compute里,client只用调用Compute来执行计算,而不用管具体的实现细节。命令的具体实现则由command的Execute()方法来调用Receiver的Operation()来实现
Command command = new CalculatorCommand(
calculator, @operator, operand );
command.Execute();
// Add command to undo list
commands.Add( command );
current++;
}
}
5.Client:
public class Client
{
public static void Main( string[] args )
{
// 创建用户对象,并发送运算请求和操作数给invoker
User user = new User();
user.Compute( '+', 100 );
user.Compute( '-', 50 );
user.Compute( '*', 10 );
user.Compute( '/', 2 );
// 执行Undo或Redo
user.Undo( 4 );
user.Redo( 3 );
}
}