命令模式

128 阅读3分钟

命令模式

将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分隔开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行存储、传递、调用、增加与管理。

⭐结构

  • 抽象命令角色——定义命令的接口,声明执行的方法
  • 具体命令角色——具体的命令,实现命令接口。通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。
  • 实现者/接收者角色——接收者,是真正去执行命令的对象。而任何一个类都能成为接收者,只要它能够实现命令要求实现的相应功能
  • 调用者/请求者角色——要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正出发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。

🌰举个例子

例如点餐:

服务员就是调用者角色,由其发起命令

厨师就是接收者角色,执行命令的对象

而点餐命令中包含着订单

UML.png

具体实现:

我们先创建订单类:

public class Order {
    //餐桌号码
    private int tableNum;
    
    //用Map来装餐品及份数
    private Map<String,Integer> foodDir = new HashMap<String,Integer>();
    //getter省略,但setter要重写一下,将菜品名和数量传入
    public void setFood(String name, int num) {
        foodDir.put(name, num);
    }
    
}

然后我们创建厨师类:

public class SeniorChef {
    //做菜
    public void makeFood(String name, int num) {
        System.out.println(num + "份" + name);
    }
}

接下来是抽象命令类:

public interface Command {
    void execute();
}

具体命令类,在命令类中定义订单类和厨师类

public class OrderCommand implements Command {
    //持有接收者对象
    private SeniorChef receiver;
    private Order order;
    
    public void execute() {
        System.out.println(order.getDiningTable()  + "桌的订单:");
        Map<String, Integer> foodDir = order.getFoodDir();
        //遍历map集合
        Set<String> keys = foodDir.keySet();
        for(String foodName:keys) {
            receiver.makeFood(foodName, foodDir.get(foodName));
        }
        System.out.println(order.getDiningTable() + "桌的饭准备完毕!");
    }
}

服务员类:

public class Waitor {
    //持有多个命令对象
    private List<Command> commands = new ArrayList<Command>();
    public void setCommand(Command cmd) {
        //将cmd对象放到命令集合中
        commands.add(cmd);
    }
    //发起命令
    public void orderUp() {
        System.out.println("服务员:大厨,订单来咯!")
        //遍历list集合,执行命令
        for(Command command : commands) {
            if(command !=null) {
                command.execute();
            }
        }
    }
}

我们现在就可以去测试类中点单啦:

先创建一个订单对象

Order order = new Order();

然后进行点单

order.setDiningTable(1);//表示第一桌

order.setFood("西红柿鸡蛋面", 1);//点一份西红柿鸡蛋面

order.setFood("可乐",1);//再来一份可乐吧

有了订单,我们就要创建做菜的厨师了

SenioChef receiver = new SeniorChef();

创建命令类将厨师和订单都传入进去

OrderCommand cmd = new OrderCommand(receiver,order);

这个命令相当于我们点好的单,现在就需要把我们点好的单交给服务员

Waitor invoke = new Waitor();

invoke.setCommand(cmd);

最后就是服务员去下达命令,让厨师做饭

invoke.orderUp();

⭐优缺点

优点:

  • 降低系统的耦合度。命令模式能将调用操作的对象与实现该操作的对象解耦
  • 增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类,它满足“开闭原则”,对扩展比较灵活。
  • 可以实现宏命令。命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令。
  • 方便实现Undo和Redo操作。命令模式可以与备忘录模式结合,实现命令的撤销与恢复。

缺点:

  • 使用命令模式可能会导致某些系统有过多的具体命令类
  • 使系统结构更复杂

⭐适用场景

  • 系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互
  • 系统需要在不同的时间指定请求、将请求排队和执行请求
  • 系统需要支持命令的撤销操作和恢复操作