想象一下,如果你要在电视机上换频道,你可以直接在电视机上操作,或是通过遥控器来操作。这就是命令模式的表现,”你”就是发送请求的对象,而电视机就是接受请求的对象,如果“你”直接和电视机接触,是相当耦合的,不利于将来的维护,所以需要遥控器(命令对象),命令模式就是在发送对象和接受对象直接插入命令对象,再回到遥控器上,上面的每一个按钮意味着一个命令,可以理解为一个命令对象,而整个遥控器就是命令对象的集合(宏命令的概念)。每个命令对象里封装了对请求对象的调用,
整个流程逐渐清晰,你(客户端)按遥控器中的某个按钮(调用命令对象的方法),此方法又调用电视机(接受对象的方法),电视机换频道(由请求对象来完成实际的任务)。
在来看下定义,命令模式:将“请求”封装成对象,以便使用不同的请求,队列或者日志来参数化其他对象,命令模式也可支持可撤销的操作。
这里讲下撤销,原理实际也简单,既然要返回原来状态,那就需要定义一个数据类型来记录状态,用String类型来表示,就够了,再进一步,如果是多次撤销呢?原来就不能使用了,要用链表或栈来存放状态。接着在命令对象中定义一个方法,如 void undo()。内容是根据状态来调用相应的方法就行了。
当然,命令对象必须要有特征来表明它是命令对象,实现命令接口,实现命令接口的方法。只能说继承是实现解耦的基本方法。类图:
(https://img-blog.csdn.net/20150306101704989)
这里可能有让人费解的地方,client这个类是干什么的,如果你是系统端,则只要编写命令接口(command)和Invoker就行了。作为客户端(client),来创建具体的Receiver和ConcrateCommand.
另一些概念
NoCommand,空对象,即继承了命令接口,却不做任何事。
命令对象的其他作用:
队列请求:原理,我个人理解啊,就是不断创建命令对象,存在stack中,然后线程来取走一个命令对象,执行它的方法,并在stack中删除这个命令对象。
日志管理:往往用于数据库还原中,先记录某个时期的数据库数据,然后记录的是操作状态。
这是代码,可能顺序有些乱
public interface Command {
public void execute();
}
public class Light {
public void on(){
System.out.println("Light is on");
}
public void off(){
System.out.println("Light is off");
}
}
public class LightOffCommand {
Light light;
public LightOffCommand(Light light){
this.light = light;
}
public void execute(){
light.off();
}
}
public class LightOnCommand implements Command{
Light light;
public LightOnCommand(Light light){
this.light = light;
}
@Override
public void execute() {
light.on();
}
public class Stereo {
public void on(){
System.out.println("stereo is on");
}
public void off(){
System.out.println("stereo is off");
}
public void setCD(){
}
public void setVolume(int v){
}
}
public class StereOnWithCDCommand implements Command{
Stereo stereo;
public StereOnWithCDCommand(){
this.stereo = stereo;
}
@Override
public void execute() {
stereo.on();
stereo.setCD();
stereo.setVolume(11);
}
}
public class NoCommand implements Command{
@Override
public void execute() {
// TODO Auto-generated method stub
}
}
public class RemoteControl {
Command[] onCommands;
Command[] offCommands;
public RemoteControl(){
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;
}
}
public void setCommand(int slot,Command onCommand,Command offCommand){
onCommands[slot] = onCommand;
offCommands[slot] = offCommand;
}
public void onButtonWasPushed(int slot){
onCommands[slot].execute();
}
public void offButtonWasPushed(int slot){
offCommands[slot].execute();
}
public String toString(){
StringBuffer stringBuff = new StringBuffer();
stringBuff.append("\n----Remote Control -----\n");
for(int i = 0; i < onCommands.length;i++){
stringBuff.append("[slot" + i + "]" + onCommands[i].getClass().getName()
+" " + offCommands[i].getClass().getName()+"\n");
}
return stringBuff.toString();
}
}
public class RemoteControlTest {
public static void main(String[] args) {
SimpleRemoteControl remote = new SimpleRemoteControl();
Light light = new Light();
LightOnCommand lightOn = new LightOnCommand(light);
remote.setCommand(lightOn);
remote.buttonWasPressed();
}
}
public class SimpleRemoteControl {
Command slot;
public SimpleRemoteControl(){
}
public void setCommand(Command command){
slot = command;
}
public void buttonWasPressed(){
slot.execute();
}
}