一、简介
1.1 模式定义
将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。命令模式是一种对象行为型模式,其别名为动作模式或事务模式。
1.2 使用场景
1)在系统需要将请求调用者和请求接受者解耦,使得调用者和接受者不直接交互。请求调用者无须知道接受者的存在,也无须知道接受者是谁,接受者无须关系何时被调用。
2)系统需要在不同的时间指定请求、将请求排队和执行请求。一个命令对象和请求的初始调用者可以有不同的生命期,换言之,最初的请求发出者可能已经不在了,而命令对象本身依然是活动的,可以通过该命令对象去调用请求接受者,而无须关心请求调用者的存在性。
3)系统需要支持命令的撤销操作和回复操作。可以将命令对象存储起来,如果客户端要撤销命令,则可以通过调用撤销方法撤销命令所产生的效果,还可以提供重新执行命令方法,以供客户端在需要时重新执行命令。
4)xit 需要将一组操作组合在一起,即支持宏命令。可以通过组合模式将命令对象组合在一起形成更大的命令对象,从而实现命令的批处理执行。
1.3 优点
1)降低系统的耦合度。由于请求者与接收者之间不存在直接引用,因此请求者与接收者之间实现完全解耦相同的请求者可以对应不同的接收者,同样,相同的接收者也可以供不同的请求者使用,两者具有良好的独立性。
2)新的命令可以很容易地加入到系统中。增加新的具体命令类不影响其他的类,因此增加新的具体命令类很容易,增加新的具体命令无须修改原有系统原代码,包括客户类代码,满足“开闭原则”,使得系统具有良好的灵活性和可扩展性。
3)可以比较容易地设计一个命令队列和宏命令(组合命令)。可以将对个命令组合在一起批量执行,实现批处理操作,在实现时可以结合组合模式。
4)可以方便地实现对请求的Undo 和 Redo。对于有些命令可以提供一个对应的逆操作命令,并将命令对象存储在集合中,从而实现对请求操作的Undo 和 Redo 操作。
1.4 缺点
使用命令模式可能会导致某些系统有过多的具体命令类。因此针对每一个命令都需要设计一个具体命令类,所以某些系统可能需要大量具体命令类,这将影响命令模式的使用。
二、示例
2.1 结构图
2.2 接收者类
public class Television {
public void open() {
System.out.println("打开电视机!");
}
public void close() {
System.out.println("关闭电视机!");
}
public void changeChannel() {
System.out.println("切换电视频道!");
}
}
2.3 抽象命令类
public interface AbstractCommand {
public void execute();
}
2.4 具体命令类TVOpenCommand (电视机打开命令类)
public class TVOpenCommand implements AbstractCommand{
private Television tv;
public TVOpenCommand() {
tv = new Television();
}
@Override
public void execute() {
tv.open();
}
}
2.5 具体命令类TVCloseCommand (电视机关闭命令类)
public class TVCloseCommand implements AbstractCommand{
private Television tv;
public TVCloseCommand() {
tv = new Television();
}
@Override
public void execute() {
tv.close();
}
}
2.6 具体命令类TvChangeCommand(电视机频道切换命令类)
public class TvChangeCommand implements AbstractCommand{
private Television tv;
public TvChangeCommand() {
tv = new Television();
}
@Override
public void execute() {
tv.changeChannel();
}
}
2.7 调用者类
public class Controller {
private AbstractCommand openCommand, closeCommand, changeCommand;
public Controller(AbstractCommand openCommand, AbstractCommand closeCommand, AbstractCommand changeCommand) {
this.openCommand = openCommand;
this.closeCommand = closeCommand;
this.changeCommand = changeCommand;
}
public void open() {
openCommand.execute();
}
public void change() {
closeCommand.execute();
}
public void close() {
changeCommand.execute();
}
}