设计模式学习笔记之命令模式
命令模式
将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。
说明:
1、命令模式将发出请求的对象和执行请求的对象解耦;
2、在被解耦的两者之间是通过命令对象进行沟通的。命令对象封装了接受者和一个或一组动作;
3、调用者通过调用命令对象的execute()发出请求,这会使得接受者的动作被调用;
4、调用者可以接受命令当做参数,甚至在运行时动态地进行;
5、命令可以支持撤销,做法事实现一个undo()方法来回到exexcute()被执行前的状态;
6、命令模式也可以用来实现日志和事物系统;
优点:
1、降低对象之间的耦合度;
2、新的命令可以很容易地加入到系统中;
3、可以比较容易地设计一个组合命令;
4、调用同一方法实现不同的功能。
缺点:
使用命令模式可能会导致某些系统有过多的具体命令类。因为针对每一个命令都需要设计一个具体命令类,因此某些系统可能需要大量具体命令类,这将影响命令模式的使用。
场景:
1、系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互;
2、系统需要在不同的时间指定请求、将请求排队和执行请求;
3、系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作;
4、系统需要将一组操作组合在一起,即支持宏命令;
例子:
录音机有播音(Play)、倒带(Rewind)和停止(Stop)功能,录音机的键盘便是请求者(Invoker)角色;Leslie是客户端角色,而录音机便是接收者角色。Command类扮演抽象命令角色,而PlayCommand、StopCommand和RewindCommand便是具体命令类。Leslie不需要知道播音(play)、倒带(rewind)和停止(stop)功能是怎么具体执行的,这些命令执行的细节全都由键盘(Keypad)具体实施。Leslie只需要在键盘上按下相应的键便可以了。
/**
* 命令接口。
* 所有的命令对象需要实现该接口。
* */
public interface Commond {
public void execute();
}
|
/**
* 录音机--命令接受者
* */
public class AudioPlayer {
public void play() {
System.out.println("播放......");
}
public void stop() {
System.out.println("暂停......");
}
public void rewind() {
System.out.println("倒带......");
}
}
|
/**
* 录音机播放命令
* 具体命令角色类
* */
public class PlayCommond implements Commond{
AudioPlayer player;
public PlayCommond(AudioPlayer player) {
this.player = player;
}
// 调用接收对象(录音机)的 play() 方法
public void execute() {
player.play();
}
}
|
/**
* 录音机倒带命令
* 具体命令角色类
* */
public class RewindCommond implements Commond{
AudioPlayer player;
public RewindCommond(AudioPlayer player) {
this.player = player;
}
// 调用接收对象(录音机)的 rewind() 方法
public void execute() {
player.rewind();
}
}
|
/**
* 录音机暂停命令
* 具体命令角色类
* */
public class StopCommond implements Commond{
AudioPlayer player;
public StopCommond(AudioPlayer player) {
this.player = player;
}
// 调用接收对象(录音机)的 stop() 方法
public void execute() {
player.stop();
}
}
|
/**
* 键盘类
* 请求者角色
* */
public class Keypad {
private Commond play;
private Commond stop;
private Commond rewind;
public void setPlayCommond(Commond play) {
this.play = play;
}
public void setStopCommond(Commond stop) {
this.stop = stop;
}
public void setRewindCommond(Commond rewind) {
this.rewind = rewind;
}
// 执行播放方法
public void play() {
play.execute();
}
// 执行暂停方法
public void stop() {
stop.execute();
}
// 执行倒带方法
public void rewind() {
rewind.execute();
}
}
|
/**
* 具体人--莱斯利
* 客户端角色
* */
public class Leslie {
public static void main(String[] args) {
// 创建接受者对象,即收音机对象
AudioPlayer player = new AudioPlayer();
// 创建命令者对象
Commond play = new PlayCommond(player);
Commond stop = new StopCommond(player);
Commond rewind = new RewindCommond(player);
// 创建请求者对象,即键盘对象
Keypad keypad = new Keypad();
keypad.setPlayCommond(play);
keypad.setStopCommond(stop);
keypad.setRewindCommond(rewind);
// 播放
keypad.play();
// 暂停
keypad.stop();
// 倒带
keypad.rewind();
}
}
|
所谓宏命令简单点说就是包含多个命令的命令,是一个命令的组合。
设想Leslie 的录音机有一个记录功能,可以把一个一个的命令记录下来,再在任何需要的时候重新把这些记录下来的命令一次性执行,这就是所谓的宏命令集功能。因此,Leslie 的录音机系统现在有四个键,分别为播音、倒带、停止和宏命令功能。此时系统的设计与前面的设计相比有所增强,主要体现在Julia类现在有了一个新方法,用以操作宏命令键。
/**
* 定义宏命令接口
* */
public interface MacroCommand extends Command{
/**
* 宏命令存储的管理方法
* 可以添加一个成员命令
*/
public void add(Command cmd);
/**
* 宏命令存储的管理方法
* 可以删除一个成员命令
*/
public void remove(Command cmd);
}
|
/**
* 具体的宏命令,对存储的若干命令进行操作
* */
public class MacroAudioCommand implements MacroCommand{
// 宏命令集合
private List<Command> cmdList = new ArrayList<Command>();
// 一次性执行宏命令集合中的各命令
public void execute() {
System.out.println("-------自动回放保存的命令-------");
for(Command cmd : cmdList) {
cmd.execute();
}
System.out.println("------------回放完毕------------");
}
public void add(Command cmd) {
cmdList.add(cmd);
}
public void remove(Command cmd) {
cmdList.remove(cmd);
}
}
|
/**
* 具体人--莱斯利
* 客户端角色
* */
public class Leslie {
public static void main(String[] args) {
// 创建接受者对象,即收音机对象
AudioPlayer player = new AudioPlayer();
// 创建命令者对象
Command play = new PlayCommond(player);
Command stop = new StopCommond(player);
Command rewind = new RewindCommond(player);
MacroCommand macro = new MacroAudioCommand();
macro.add(play);
macro.add(rewind);
macro.add(stop);
macro.execute();
}
}
|
参考资料:《Head First 设计模式》