命令模式

2017-03-10  本文已影响0人  炫迈哥

先上headfirst的类图和代码:


public class TV {
    public void on(){}
    public void off(){}
    public void setInputChannel(){}
    public void setVolume(){}
}

public class CeilingFan {
    public void high(){}
    public void medium(){}
    public void low(){}
    public void off(){}
    public void getSpeed(){}
}

public class OutDoorLight {
    public void on(){}
    public void off(){}
}
public interface Command {
    public void execute();
    public void undo();
}

public class LightOnCommand implements Command {
    Light light;

    public LightOnCommand(Light light){
        this.light = light;
    }

    @Override
    public void execute() {
        light.on();
    }

    @Override
    public void undo() {
        light.off();
    }
}
public class RemoteControl {
    Command[] onCommands;
    Command[] offCommands;
    // 前一个命令将被记录在这里
    Command undoCommand;

    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;
        }
        // 一开始并没有所谓的“前一个命令”,所以将它设置成NoCommand
        undoCommand = noCommand;
    }

    public void setCommand(int slot, Command onCommand, Command offCommand) {
        onCommands[slot] = onCommand;
        offCommands[slot] = offCommand;
    }

    // 当按下按钮,我们取得这个命令,并优先执行它,然后将它记录在undoCommand中。
    public void onButtonWasPushed(int slot){
        onCommands[slot].execute();
        undoCommand = onCommands[slot];
    }

    public void offButtonWasPushed(int slot){
        offCommands[slot].execute();
        undoCommand = offCommands[slot];
    }
    
    // 当按下撤销按钮,我们调用undoCommand实例变量的undo()方法,就可以倒转前一个命令
    public void undoButtonWasPushed(){
        undoCommand.undo();
    }
}

如果不使用命令模式

public class RemoteControl {
    TV tv = new TV();
    ... //创建一堆实际执行操作的类
    public void onButtonWasPushed(int slot){
        if(slot == 1){
            tv.on();
        }else if() //....一堆if  else判断
    }
}

1.RemoteControl直接依赖所有具体执行类,高度耦合。
2.如果现在需要新增或者删除一种操作,必须修改RemoteControl,并在if else中添加或删除一条分支,很难扩展
3.每个遥控器插槽和具体操作已经绑死,无法在运行时变更。
4.要实现undo不是很优雅,虽然也可以存储最后一次按键的slot和按键类型(on or off)来进行undo,代码将会又臭又长,极难维护(因为有的比如吊扇他的转速有五档。。。)
5.要实现多种操作组合也比较困难,能想到的方式是:妈的,想不到啥能说出口的。。。

命令模式到底怎么牛逼了

public class MultiCommand implements Command {
    private Command[] commands;

    public MultiCommand(Command[] commands){
        this.commands = commands;
    }

    @Override
    public void execute() {
        for(Command command : commands){
            command.execute();
        }
    }

    @Override
    public void undo() {
         for(Command command : commands){
            command.undo();
        }
    }
}

一些问题

public interface ICommand<T> {
    void execute(T parameter);
}
//或者
public interface ICommand<T> {
    void execute(Object... parameter);
}

这样一来invoke类就必须了解每个command的参数类型等逻辑,所谓的命令模式解耦就完全被破坏了,所以应该将参数放在每个command内部,通过构造器或者setter方式设值,设值动作则由client类进行。

public class ParamterCommand implements Command {
  private TV tv;
  private long time; //灯开多久,参数通过构造器注入
  public MultiCommand(TV tv, long time){
      this.tv = tv;
  }

  @Override
  public void execute() {
      tv.on(this.time);
  }

  @Override
  public void undo() {
       //....
  }
}

命令模式使用场景

上一篇 下一篇

猜你喜欢

热点阅读