十七.行为型设计模式——Command Pattern(命令模式)
-
定义
将一个请求封装成一个对象,因此可以参数化多个客户的不同请求,将请求排队,记录请求日志,并且支持撤销操作。
UML类图如下:
其中类和对象之间的关系为:
1. Command(抽象命令):声明执行操作的一个接口。
2. ConcreteCommand(具体命令类):将一个接收者对象绑定于一个动作;实现Execute方法,以调用接收者的相关的操作(Action)。
3. Client(客户应用程序):创建一个具体命令类的对象,并且设定它的接收者。
4. Invoker(调用者):要求一个命令对象执行一个请求。
5. Receiver(接收者):知道如何执行关联请求的相关操作。
典型应用的顺序图:
-
实例1——简单计算器
把命令模式应用于一个简单计算器程序,可以产生无限个Redo(重做)和Undo(取消)操作,UML类图如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//抽象命令类
abstract class Command
{
abstract public void Execute();
abstract public void UnExecute();
}
//具体命令类
class CalculatorCommand : Command
{
private 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; }
}
public override void Execute()
{
calculator.Operation(@operator, operand);
}
public override 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;
}
return undo;
}
}
//接收者
class Calculator
{
private int total = 0;
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);
}
}
//调用者
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);
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);
for (int i = 0; i < levels; i++)
{
if (current >0)
{
((Command)commands[--current]).UnExecute();
}
}
}
public void Compute(char @operator, int operand)
{
//生成命令并且执行它
Command command = new CalculatorCommand(calculator, @operator, operand);
command.Execute();
//在取消列中增加这个命令
commands.Add(command);
current++;
}
}
//客户应用测试
class Client
{
[STAThread]
static void Main(string[] args)
{
User user = new User();
user.Compute('+', 100);
user.Compute('-', 50);
user.Compute('*', 10);
user.Compute('/', 2);
//取消,重做一些命令
user.Undo(4);
user.Redo(3);
Console.Read();
}
}
abstract class Command
{
abstract public void Execute();
abstract public void UnExecute();
}
//具体命令类
class CalculatorCommand : Command
{
private 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; }
}
public override void Execute()
{
calculator.Operation(@operator, operand);
}
public override 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;
}
return undo;
}
}
//接收者
class Calculator
{
private int total = 0;
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);
}
}
//调用者
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);
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);
for (int i = 0; i < levels; i++)
{
if (current >0)
{
((Command)commands[--current]).UnExecute();
}
}
}
public void Compute(char @operator, int operand)
{
//生成命令并且执行它
Command command = new CalculatorCommand(calculator, @operator, operand);
command.Execute();
//在取消列中增加这个命令
commands.Add(command);
current++;
}
}
//客户应用测试
class Client
{
[STAThread]
static void Main(string[] args)
{
User user = new User();
user.Compute('+', 100);
user.Compute('-', 50);
user.Compute('*', 10);
user.Compute('/', 2);
//取消,重做一些命令
user.Undo(4);
user.Redo(3);
Console.Read();
}
}
-
优势和缺陷
命令模式分离了接受请求的对象与实现处理请求工作的对象,这样,已经存在的类可以保持不变,使得增加新类的工作更简单。例如,很多软件的宏命令就提高了系统的自动化程度。
命令模式还可以分离用户界面和业务对象,降低系统的耦合度。
但是,命令模式最主要的缺陷就是,类的数量增加了,系统变得更复杂,程序的调试工作也相应变得困难。
-
应用情景
下面的情景很适合应用命令模式:
1. 当你需要与工作相关的对象来作为参数。
2. 你需要在不同的时间创建请求,生成请求队列,执行请求。
3. 你需要支持取消、保存修改日志或处理事务功能。
4. 你需要支持宏命令。