说起命令模式,第一个引入脑海的是Java的GUI图形化编程,其中就采用了命令模式处理事件。在这个事件处理模型里面,命令对象实现AWT的Listener接口,相当于命令接口。为把一个命令对象与一个AWT的构件连接起来,需要把它登记成一个事件的Listener。构件只认识Listener接口,而不在乎接口是怎么实现的。我们编写一个俄罗斯方块游戏,菜单栏等的事件如下:
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
JMenu setMenu = new JMenu("Set");
JMenu helpMenu = new JMenu("Help");
setMenu.setMnemonic('s');
setMenu.setMnemonic('H');
menuBar.add(setMenu);
menuBar.add(helpMenu);
setMenu.add(startMI);
setMenu.add(pauseMI);
setMenu.addSeparator();
setMenu.add(loadMI);
setMenu.add(saveMI);
setMenu.add(recordMI);
setMenu.addSeparator();
setMenu.add(speedMenu);
setMenu.addSeparator();
setMenu.add(exitMI);
ButtonGroup group = new ButtonGroup();
group.add(speedMI1);
group.add(speedMI2);
group.add(speedMI3);
group.add(speedMI4);
group.add(speedMI5);
speedMenu.add(speedMI1);
speedMenu.add(speedMI2);
speedMenu.add(speedMI3);
speedMenu.add(speedMI4);
speedMenu.add(speedMI5);
startMI.addActionListener(new StartAction());
pauseMI.addActionListener(new PauseAction());
loadMI.addActionListener(new LoadAction());
saveMI.addActionListener(new SaveAction());
recordMI.addActionListener(new RecordAction());
exitMI.addActionListener(new ExitAction());
speedMI1.addActionListener(new SpeedAction());
speedMI2.addActionListener(new SpeedAction());
speedMI3.addActionListener(new SpeedAction());
speedMI4.addActionListener(new SpeedAction());
speedMI5.addActionListener(new SpeedAction());
helpMenu.add(aboutMI);
aboutMI.addActionListener(new AboutAction());
具体事件实现如:
... ...
private class ExitAction implements ActionListener {
public void actionPerformed(ActionEvent event) {
int result = JOptionPane.showConfirmDialog(RussiaGameFrame.this,
"Are you sure quit?", "俄罗斯方块", JOptionPane.YES_NO_OPTION);
if (result == JOptionPane.YES_OPTION) {
System.exit(0);
}
}
}
private class AboutAction implements ActionListener {
public void actionPerformed(ActionEvent event) {
String string = "说明:\n1.按左键向左移动\n" + "2.按右键向右移动\n" + "3.按向上键翻滚\n"
+ "4.按向下键加速下降\n" + "5.按空格键下降到最底部";
JOptionPane.showMessageDialog(RussiaGameFrame.this, string);
}
}
... ...
接下来,我们来看一下命令模式的一些知识吧。
意图
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
结构
命令模式的基本结构如下:
这里涉及到的参与者有如下几种:
参与者如何协作?
1、Client创建一个ConcreteCommand对象并指定它的Receiver对象
2、某Invoker对象存储该ConcreteCommand
3、该Invoker通过调用Command对象的Execute操作来提交一个请求。若该命令是可撤销的,ConcreteCommand就在执行Execute操作之前存储当前状态以用于取消该命令。
ConcreteCommand对象对调用它的Receiver的一些操作以执行该请求。
接下来以一个空调遥控器对空调进行打开、调温、关闭操作,来说明一下命令模式。
package com.wangmengjun.tutorial.designpattern.command;
public interface Command {
void execute();
}
打开命令
package com.wangmengjun.tutorial.designpattern.command;
public class TurnOnCommand implements Command {
private Receiver receiver;
public TurnOnCommand(Receiver receiver) {
this.receiver = receiver;
}
@Override
public void execute() {
receiver.turnOn();
}
}
升温命令
package com.wangmengjun.tutorial.designpattern.command;
public class IncreaseTempCommand implements Command {
private Receiver receiver;
public IncreaseTempCommand(Receiver receiver) {
this.receiver = receiver;
}
@Override
public void execute() {
receiver.increaseTemp();
}
}
降温命令
package com.wangmengjun.tutorial.designpattern.command;
public class DescTempCommand implements Command {
private Receiver receiver;
public DescTempCommand(Receiver receiver) {
this.receiver = receiver;
}
@Override
public void execute() {
receiver.descTemp();;
}
}
关闭命令
package com.wangmengjun.tutorial.designpattern.command;
public class TurnOffCommand implements Command {
private Receiver receiver;
public TurnOffCommand(Receiver receiver) {
this.receiver = receiver;
}
@Override
public void execute() {
receiver.turnOff();
}
}
package com.wangmengjun.tutorial.designpattern.command;
public class AirConditionRemoteControl {
private Command command;
public AirConditionRemoteControl(Command command) {
this.command = command;
}
public void click() {
command.execute();
}
/**
* @param command the command to set
*/
public void setCommand(Command command) {
this.command = command;
}
}
package com.wangmengjun.tutorial.designpattern.command;
public interface Receiver {
void turnOn();
void turnOff();
void increaseTemp();
void descTemp();
}
package com.wangmengjun.tutorial.designpattern.command;
public class AirConditionReceiver implements Receiver{
@Override
public void turnOn() {
System.out.println("打开空调");
}
@Override
public void turnOff() {
System.out.println("关闭空调");
}
@Override
public void increaseTemp() {
System.out.println("空调温度升高一度");
}
@Override
public void descTemp() {
System.out.println("空调温度降低一度");
}
}
package com.wangmengjun.tutorial.designpattern.command;
public class Client {
public static void main(String[] args) {
Receiver receiver = new AirConditionReceiver();
TurnOnCommand turnOnCommand = new TurnOnCommand(receiver);
//1、模拟点击打开按钮
AirConditionRemoteControl invoker = new AirConditionRemoteControl(turnOnCommand);
invoker.click();
//2、模拟点击升温按钮
invoker.setCommand(new IncreaseTempCommand(receiver));
invoker.click();
invoker.click();
//3、模拟点击降溫按钮
invoker.setCommand(new DescTempCommand(receiver));
invoker.click();
//4、模拟点击关闭按钮
invoker.setCommand(new TurnOffCommand(receiver));
invoker.click();
}
}
输出结果:
打开空调
空调温度升高一度
空调温度升高一度
空调温度降低一度
关闭空调就这样,一个简单的命令模式的示例就完成了。
命令模式的优缺点:
优点:
(1):命令模式把请求一个操作的对象与知道怎么执行一个操作的对象分割开。
(2):你可以把命令对象聚合在一起,合成为合成命令。
(3):由于加进新的具体命令类不影响其他的类,因此增加新的具体命令类很容易
缺点:
(1):使用命令模式会导致某些系统有过多的具体命令类。某些系统可能需要几十个,几百个甚至几千个命令类,这会使命令模式在这样的系统变得不实际。
参考
[1]. 阎宏. Java与模式.电子工业出版社
[2]. Erich Gamma. 设计模式-可复用面向对象软件的基础. 机械工业出版社.