记录《Head First 设计模式》
参考 Graphic Design Patterns网站 与 深入设计模式
目的是检查学习情况,同时方便以后复习。
文章目录
一、简述
1-1 意图
命令模式 CommandPattern:
Encapsulate a request as an Object .
Allows the Parameterization of Clients with different requests.
Allows Saving the request in a queue.
Proving an object-oriented callbact.
将请求封装成对象,从而使用不同的请求对客户进行参数化、对请求排队(队列)或记录请求日志。命令模式也可以支持撤销操作。又称为动作模式或事务模式。该模式是一种 行为型模式 。
1-2 动机
使请求调用者与接收者完全解耦,请求调用者与接收者没有直接引用关系,发送请求的对象只需知道如何发送请求,无法知道如何完成请求。这就是命令模式的动机。
1-3 模式结构
- Client:客户
- Invoker:调用者
- Command:命令接口
- ConcreteCommand:具体命令者
- Receiver:接收者
1-4 时序图
二、代码示例:
1-1 封装请求
设计一款家电自动化遥控器,其中包括客厅灯开关、电视开关(开包含声音大小默认为7)、所有灯开关、空调开关(开包含温度为24、开启冷风热风)、吊扇开(高中低)关等。下面以灯为具体命令者举例。
命令接口:
/// <summary>
/// 命令模式:将“请求”封装成对象
/// </summary>
public interface Command
{
/// <summary>
/// 执行请求
/// </summary>
public void Execute();
}
具体命令者----灯开操作
/// <summary>
/// 具体命令者:灯开操作
/// </summary>
public class LightOnCommand : Command
{
/// <summary>
/// 对象
/// </summary>
Light Light;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="light"></param>
public LightOnCommand(Light light)
{
Light = light;
}
/// <summary>
/// 执行请求
/// </summary>
public void Execute()
{
// 接收者执行相关请求
Light.on();
}
}
接收者----灯
/// <summary>
/// 接受者:执行命令相关操作
/// </summary>
public class Light
{
/// <summary>
/// 开灯
/// </summary>
public void on()
{
Console.WriteLine("Light is ON");
}
/// <summary>
/// 关灯
/// </summary>
public void off()
{
Console.WriteLine("Light is OFF");
}
}
调用者----简单控制
/// <summary>
/// 调用者
/// </summary>
public class SimpleRemoteControl
{
/// <summary>
/// 命令接口:存储命令者
/// </summary>
Command _command;
public SimpleRemoteControl() { }
/// <summary>
/// 命令对象存储在调用者中
/// </summary>
/// <param name="command"></param>
public void SetCommand(Command command)
{
_command = command;
}
/// <summary>
/// 调用命令模式的执行请求方法
/// </summary>
public void ButtonWasPressed()
{
_command.Execute();
}
}
执行命令模式
public class Program
{
public static void Main(string[] arg)
{
// 调用者
SimpleRemoteControl control = new SimpleRemoteControl();
// 接收者
Light light = new Light();
// 具体命令者
Command LightCommand = new LightOnCommand(light);
// 向调用者传递命令者
control.SetCommand(LightCommand);
// 调用命令接口执行请求
control.ButtonWasPressed();
}
}
执行结果
1-2 撤销
命令模式支持撤销操作,下面让我们在遥控器上加上撤销功能。
功能使用举例:客厅灯初始状态为关状态,当你按下遥控器《开》操作,灯会打开。按下《撤销》按钮,那就就会回到执行《开》操作之前。下面让我们看一下如果执行撤销操作。
具体命令类----灯开操作
/// <summary>
/// 具体命令者:灯开操作
/// </summary>
public class LightOnCommand : Command
{
/// <summary>
/// 对象
/// </summary>
Light Light;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="light"></param>
public LightOnCommand(Light light)
{
Light = light;
}
/// <summary>
/// 执行请求
/// </summary>
public void Execute()
{
//接受者执行相关请求
Light.on();
}
/// <summary>
/// 撤销功能
/// </summary>
public void Undo()
{
Light.off();
}
}
具体命令类----灯关操作
/// <summary>
/// 具体命令类 -灯关操作
/// </summary>
public class LightOffCommand : Command
{
/// <summary>
/// 对象
/// </summary>
Light Light;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="light"></param>
public LightOffCommand(Light light)
{
Light = light;
}
/// <summary>
/// 执行请求
/// </summary>
public void Execute()
{
//接受者执行相关请求
Light.off();
}
/// <summary>
/// 撤销功能
/// </summary>
public void Undo()
{
Light.on();
}
}
调用者----遥控器
/// <summary>
/// 调用者-遥控器
/// </summary>
public class RemoteControlWithUndo
{
Command[] onCommands;
Command[] offCommands;
Command undoCommand;
public RemoteControlWithUndo()
{
onCommands = new Command[7] ;
offCommands = new Command[7] ;
Command noCommand = new NoCommand();
for (int i = 0; i < 7; i++)
{
onCommands[i] = noCommand;
offCommands[i] = noCommand;
}
undoCommand = noCommand;
}
/// <summary>
/// 遥控按钮
/// </summary>
/// <param name="solt"></param>
/// <param name="onCommand"></param>
/// <param name="offCommand"></param>
public void SetCommand(int solt, Command onCommand = null, Command offCommand = null)
{
onCommands[solt] = onCommand;
offCommands[solt] = offCommand;
}
/// <summary>
/// 某个按钮开
/// </summary>
/// <param name="slot"></param>
public void onButtonWasPushed(int slot)
{
onCommands[slot].Execute();
undoCommand = onCommands[slot];
}
/// <summary>
/// 某个按钮关
/// </summary>
/// <param name="slot"></param>
public void offButtonWasPushed(int slot)
{
offCommands[slot].Execute();
undoCommand = offCommands[slot];
}
/// <summary>
/// 撤销
/// </summary>
/// <param name="slot"></param>
public void undoButtonWasPushed(int slot)
{
Console.WriteLine("撤销");
undoCommand.Undo();
}
}
具体命令类----初始默认
/// <summary>
/// 没有命令:初始状态
/// </summary>
public class NoCommand : Command
{
public void Execute()
{
Console.WriteLine("默认状态");
}
public void Undo()
{
Console.WriteLine("无撤销");
}
}
执行命令模式
public class Program
{
public static void Main(string[] arg)
{
// 调用者
RemoteControlWithUndo control = new RemoteControlWithUndo();
// 接收者
Light light = new Light();
// 具体命令者
Command LightOnCommand = new LightOnCommand(light);
Command LightOffCommand = new LightOffCommand(light);
// 向调用者传递命令者
control.SetCommand(0, LightOnCommand, LightOffCommand);
// 调用命令接口执行请求
control.onButtonWasPushed(0);
// 撤销
control.undoButtonWasPushed(0);
Console.WriteLine();
Console.WriteLine();
// 调用命令接口执行请求
control.offButtonWasPushed(0);
// 撤销
control.undoButtonWasPushed(0);
}
}
执行结果
三、总结
1-1 模式分析
命令模式的本质是对命令进行封装,将发出命令的责任与执行命令的责任分隔开。
- 每一个命令都是一个操作:请求的一方发布请求,要求执行一个操作;接收的一方收到请求,并执行操作。
- 命令模式允许请求的一方与接收请求的一方独立开,使得请求的一方不必知道接收请求一方的接口,更不比知道请求是如何被接收,以及操作是否被执行、何时执行,以及怎么被执行。
- 命令模式使请求本身成为一个对象,这个对象和其他对象一样可以被存储和传递。
- 命令模式的关键在于引入了抽象命令接口,且发送者针对命令接口编程,只有实现抽象命令接口的具体命令才能与接收者相关联。
1-2 模式优缺点
优点:
- 单一职责原理。可以解耦调用者与接收者。
- 开闭原则。在不修改已有客户端代码的情况下在程序中创建新的命令。
- 可以实现撤销和恢复功能。
- 可以实现操作的延迟执行。
- 可以比较容易地设计一个命令列或宏命令(组命令)
缺点:
- 代码变得更加复杂,因为在调用者与接收者之间增加了一个全新的层次。
- 使用命令模式可能会导致某些系统有过多的具体命令类(针对请求接收者的每一个调用都需要设计一个具体命令类)。
1-3 适合场景
适用场景:
- 系统中需要将调用者与接收者进行解耦,使调用者与接收者之间不交互。
- 请求者需要在不同的时间指定请求、将请求排队和执行请求。
- 系统需要撤销(Undo)和 恢复(Redo)操作。
- 系统需要将一组操作组合在一起,即支持宏命令。
- 日志请求:所有动作记录在日志中,并在系统死机后。重新调用这些动作恢复到之前的状态。
1-4 模式应用
模式应用:
- 系统提供宏命令功能,例如:UNIX平台下的Shell编程,可以将多条命令封装在一个命令对象中,只需要一条简单的命令即可执行一个命令序列
- 队列请求:日志安排(Scheduler)、线程池、工作队列等。
1-5 总结
- 命令模式将发出请求的对象(调用者)与执行请求的对象(接收者)解耦。
- 在被解耦的两者之间是通过命令模式进行沟通。命令对象封装了接收者和一个或一组动作。
- 命令模式的本质是对命令进行封装,将发出命令的责任和执行命令的责任分隔开。命令模式使请求本身成为一个对象,这个对象和其他对象一样可以被存储和传递。
- 宏命令是命令的一种简单的延伸,允许调用多个命令,同样宏命令也支持撤销操作。
- 实际操作时,很常见使用“聪明”命令对象,也就是直接实现了请求,而不是将工作委托给接收者。
- 命令也可以实现日志和事务系统。