定义

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

当对象A中的方法需要调用对象B中的方法时我们可以直接在A方法中创建一个B对象,然后通过B对象来直接调用。方法调用我们一般都是这么写,但是这么写会使得A要强依赖于B,名利模式就是为了解耦A与B。

对象方法调用

@Data
public class Order {
    /** 餐桌号 */
    private int diningTable;
    /** 食物名和数量 */
    private Map<String, Integer> foodDir = new HashMap<>();

    public void putFood(String name, int num) {
        foodDir.put(name, num);
    }
}
/** 厨师 */
public class Chef {
    public void makeFood(String foodName, int num) {
        System.out.println("制作食物:" + foodName + ", 份数:" + num);
    }
}
/**
 * 服务员点餐
 */
public class Waiter {
    public void orderFood(Order order) {
        // 1.创建厨师对象
        Chef chef = new Chef();
        order.getFoodDir().forEach((foodName, num) -> {
            // 2.调用厨师中的制作食物方法
            chef.makeFood(foodName, num);
        });
    }
}
public class Test {
    public static void main(String[] args) {
        Order order = new Order();
        order.setDiningTable(1);
        order.putFood("鸡蛋面", 1);
        order.putFood("可乐", 2);

        Waiter waiter = new Waiter();
        waiter.orderFood(order);
    }
}

方法的调用说破天也是通过对象.方法(实参); 这种方式调用的,如chef.makeFood(foodName, num)
而命令模式最终也将会使用对象.方法(实参); 这种方式来调用,只不过,命令模式会先将 对象参数封装成一个对象(Command),然后将这个对象放到List先暂存起来,等需要的时候再执行对象.方法(实参);

命令模式只是将方法的调用预先暂存起来(调用的对象和方法的实参还是要预先准备好的),等到需要执行的时候再执行而不是立即执行。

案例

餐馆下单案例:

  • 顾客把订单交给服务员
  • 服务员将订单交给厨师
  • 厨师根据订单准备餐点
// 抽象命令角色
public interface Command {
    void execute();
}


/**
 * 具体命令:封装调用者和方法参数列表,实现Command中的execute()来最后调用对象的真正方法
 */
public class OrderCommand implements Command {

    /** 方法调用对象 */
    private Chef chef;
    /** 方法参数 */
    private Order order;


    public OrderCommand(Chef chef, Order order) {
        this.chef = chef;
        this.order = order;
    }


    /**
     * 调用对象的指定的方法
     */
    @Override
    public void execute() {
        order.getFoodDir().forEach((foodName, num) -> {
            chef.makeFood(foodName, num);
        });
    }
}

服务员:不再直接调用厨师对应的方法,而是暂缓客户端传递过来的命令对象,然后统一执行命令中的方法。

/**
 * 服务员(请求者角色)
 */
public class Waitor {

    private List<Command> commands = new ArrayList<>();

    public void setCommand(Command command) {
        commands.add(command);
    }

    public void orderUp() {
        System.out.println("服务员:大厨,来订单啦");
        for (Command command : commands) {
            if (command != null) {
                command.execute();
            }
        }
    }
}
public class Client {
    public static void main(String[] args) {
        // 对象
        Chef chef = new Chef();

        // 方法实参
        Order order = new Order();
        order.setDiningTable(1);
        order.putFood("鸡蛋面", 1);
        order.putFood("可乐", 2);

        // 封装调用对象和实参
        OrderCommand cmd = new OrderCommand(chef, order);

        // 暂缓命令,并执行命令列表
        Waitor waitor = new Waitor();
        waitor.setCommand(cmd);
        waitor.orderUp();
    }
}

对象方法的调用本来一句话就能解决的事对象.方法(实参); ,为啥要活生生的拆的骨肉分离呢?为了解耦感觉已经走🔥入魔了。

优缺点

优点:

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

缺点:

  • 使用名利模式可能会造成某些系统有过多的具体命令类。
  • 系统结构更加复杂。

使用场景

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

JDK源码中的运用

// 抽象命令角色
public interface Runnable {
    public abstract void run();
}
// 具体命令
public class Thread implements Runnable {

	private Runnable target;

	 
	public synchronized void start() {
        start0();
    }

	private native void start0();

}