设计模式(十九)命令模式

436 阅读4分钟

命令模式的定义:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。

我们来看这么一个例子:我们去一家烧烤店吃饭。我们会向服务员点烧烤,服务员会拿笔记本记录我们点的菜品,然后去后厨告诉厨师要烤哪些东西。期间我们还可能会加菜或者取消一些已经点的菜,服务员肯定也会拿笔记本记下来,然后告诉后厨,最后,根据笔记本记得内容和我们算钱。

命令模式整个模式就是围绕这个命令,等于例子中的笔记本上记的内容,烤一串鸡腿,烤两个鸡翅,这些都是命令。命令要有哪些功能,要有随时添加删减的功能,并且命令种类多样。命令还要有执行者,例子中执行者就是烧烤者厨师。所以,在命令类中就要指定谁执行。

先设计简单的类:烧烤者

Barbecuer:

public class Barbecuer {
    public void bakeMutton(){
        System.out.println("烧烤厨师:我烤了个羊腿");
    }
    public void bakeChickenWing(){
        System.out.println("烧烤厨师:我烤了个鸡翅");
    }
}

设计命令类:

Command

public  abstract class Command {
 
    protected Barbecuer receiver;
 
    public Command(Barbecuer receiver){
        this.receiver = receiver;
    }
    /*执行命令*/
    public abstract  void excuteCommand();
 
 
}

这是一个抽象命令父类,只需确定执行命令者是谁。抽象类不能实例化。这里写了一个带参构造函数,就规定继承此抽象方法的之类必须也有同样的代参构造函数!!即此举的目的是规定子类引用的执行者和父类统一!

下面两个类是具体的命令类,执行具体的行为

BakeChickenWingCommand:烤鸡翅命令

public class BakeChickenWingCommand extends Command {
 
 
    public BakeChickenWingCommand(Barbecuer receiver) {
        super(receiver);
    }
 
    @Override
    public void excuteCommand() {
        receiver.bakeChickenWing();
    }
}

BakeMuttonCommand:烤鸡腿命令:

public class BakeMuttonCommand extends Command{
 
 
    public BakeMuttonCommand(Barbecuer receiver) {
        super(receiver);
    }
 
    @Override
    public void excuteCommand() {
        receiver.bakeMutton();
    }
}

服务员类:Waiter

public class Waiter {
    private List<Command> orders = new ArrayList<Command>();
    /*设置订单*/
    public void setOrder(Command command){
        if(command == null){
            System.out.println("服务员:不好意思,没有鸡翅了!");
        }else{
            orders.add(command);
            System.out.println("增加订单" + new Date());
        }
    }
 
    /*取消订单*/
    public void CancelOrder(Command command){
        orders.remove(command);
        System.out.println("取消订单" + new Date());
    }
    /*通知全部执行*/
    public void Notify(){
        for (Command order : orders) {
            order.excuteCommand();
        }
    }
}

由服务员去执行命令的创建和删除,因为是多个命令,所以要创建一个List.

测试Main:

public class Main {
    public static void main(String[] args) {
        Barbecuer boy = new Barbecuer();//命令执行者
        Command bakeMuttonCommand1 = new BakeChickenWingCommand(boy);
        Command bakeMuttonCommand2 = new BakeChickenWingCommand(boy);
        Command bakeChickenWingCommand1 = new BakeChickenWingCommand(boy);
        Command bakeChickenWingCommand2 = new BakeChickenWingCommand(boy);
        Waiter girl = new Waiter();
        girl.setOrder(bakeMuttonCommand1);
        girl.setOrder(bakeMuttonCommand2);
        girl.setOrder(bakeChickenWingCommand1);
        girl.setOrder(bakeChickenWingCommand2);
        girl.Notify();
    }
}

注意:假如A桌的客人点了一个鸡翅,b桌的客人也点了一个鸡翅,我们不能直接

Command bakeMuttonCommand = new BakeChickenWingCommand(boy);
girl.setOrder(bakeMuttonCommand);//A桌的鸡翅
girl.setOrder(bakeMuttonCommand);//B桌的鸡翅

假如此时A桌要取消这个命令,即鸡翅不要了

你要撤销这个命令

girl.cancelOrder(bakeMuttonCommand);

英文A,B桌点菜时,添加的是同一个命令。撤销的时,调用的是List.remove(Object o),因为引用的是同一个对象,A.B桌点的鸡翅都会被取消.所以创建的命令要不能随意使用。

输出结果:

增加订单Fri May 25 10:57:32 CST 2018
增加订单Fri May 25 10:57:32 CST 2018
增加订单Fri May 25 10:57:32 CST 2018
增加订单Fri May 25 10:57:32 CST 2018
烧烤厨师:我烤了个鸡翅
烧烤厨师:我烤了个鸡翅
烧烤厨师:我烤了个鸡翅
烧烤厨师:我烤了个鸡翅

命令模式的构造图:

命令模式的优点:

1.它能比较容易的设计一个命令队列,

2.在需要的情况下,可以比较容易地将命令记入日志。

3.允许接收请求的一方决定是否要否决请求。

4.可以容易的实现对请求的撤销和重做。

5.由于加进新的具体命令类不影响其他的类,因此增加新的具体命令类很容易。

6.命令模式把一个请求一个操作的对象与回到怎么执行一个操作的对象割开。

只有在真正需要撤销恢复等操作功能的时候,把原理的代码重构为命令模式才有意义。

note:每一次经历,都是一种领悟,在这个过程中一点点的蜕变,变成了现在的自己。