Java-命令-设计模式(十七)

说明

在软件系统中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”。但在某些场合,比如要对行为进行“记录、撤销/重做、事务”等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将“行为请求者”与“行为实现者”解耦?将一组行为抽象为对象,实现二者之间的松耦合。这就是命令模式(Command)。

UML
uml
角色:
Command:
定义命令的接口,声明执行的方法。
ConcreteCommand:
命令接口实现对象,是“虚”的实现;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。
Receiver:
接收者,真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。
Invoker:
要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。
Client:
创建具体的命令对象,并且设置命令对象的接收者。注意这个不是我们常规意义上的客户端,而是在组装命令对象和接收者,或许,把这个Client称为装配者会更好理解,因为真正使用命令的客户端是从Invoker来触发执行。

代码

以客人到店点餐为例,服务员记录订单后下单,后厨做菜。
此例取自《大话设计模式》,代码如下。

Command角色,抽象命令

/**
 * @author ctl
 * @date 2021/1/28
 * 抽象命令,Command
 */
public abstract class Command {

    protected Barbecuer barbecuer;

    protected String type;

    public Command(Barbecuer barbecuer) {
        this.barbecuer = barbecuer;
    }

    public String getType() {
        return this.type;
    }

    abstract public void executeCommand();
}

ConcreteCommand角色,烤翅

/**
 * @author ctl
 * @date 2021/1/28
 * 烤翅命令,ConcreteCommand
 */
public class BakeChickenWingCommand extends Command {

    public BakeChickenWingCommand(Barbecuer barbecuer) {
        super(barbecuer);
        type = "烤翅";
    }

    @Override
    public void executeCommand() {
        barbecuer.bakeChickenWing();
    }
}

ConcreteCommand角色,烤串

/**
 * @author ctl
 * @date 2021/1/28
 * 烤串命令,ConcreteCommand
 */
public class BakeMuttonCommand extends Command {

    public BakeMuttonCommand(Barbecuer barbecuer) {
        super(barbecuer);
        type = "烤串";
    }

    @Override
    public void executeCommand() {
        barbecuer.bakeMutton();
    }
}

Receiver角色,后厨烧烤师傅

/**
 * @author ctl
 * @date 2021/1/28
 * 后厨烧烤师傅,Receiver
 */
public class Barbecuer {

    public void bakeMutton() {
        System.out.println("烤肉串");
    }

    public void bakeChickenWing() {
        System.out.println("烤鸡翅");
    }
}

Invoker角色,服务员

/**
 * @author ctl
 * @date 2021/1/28
 * 服务员,Invoker
 */
public class Waiter {

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

    public void setOrder(Command command) {
        if ("烤翅".equals(command.getType())) {
            System.out.println("服务员:没有鸡翅了,请点别的烧烤");
        } else {
            list.add(command);
            System.out.println("增加订单:" + command.getType() +
                    " 时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        }
    }

    public void cancelOrder(Command command) {
        list.remove(command);
        System.out.println("取消订单:" + command.getType() +
                " 时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
    }

    public void notifyCommand() {
        for (Command command : list) {
            command.executeCommand();
        }
    }
}

Client角色,测试类

/**
 * @author ctl
 * @date 2021/1/28
 * Client
 */
public class CommandMain {

    public static void main(String[] args) {
        // 准备工作
        Barbecuer barbecuer = new Barbecuer();
        Command backMutton = new BakeMuttonCommand(barbecuer);
        Command backChickenWing = new BakeChickenWingCommand(barbecuer);
        Waiter waiter = new Waiter();

        // 下单
        waiter.setOrder(backMutton);
        waiter.setOrder(backMutton);
        waiter.setOrder(backMutton);
        waiter.setOrder(backChickenWing);

        // 取消订单
        waiter.cancelOrder(backMutton);

        // 执行
        waiter.notifyCommand();
    }
}

结果
结果
可以看到,行为请求者waiter与行为实现者Barbecuer没有直接耦合在一起,并且可以对行为进行记录,撤销等。

总结

命令模式有以下几个优点:
1、命令模式比较容易实现一个命令队列。
2、在需要的情况下,命令模式可以较容易地将命令记入日志。
3、允许接收请求的一方决定是否要否决请求。
4、容易对请求实现撤销和重做。
5、新增加命令类不会影响其他命令,易于扩展。
6、行为请求者与行为实现者解耦合。

适用场景:
1、系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。
2、系统需要在不同的时间指定请求、将请求排队和执行请求。
3、系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。
4、系统需要将一组操作组合在一起,即支持宏命令。

敏捷开发原则告诉我们,不要为代码添加基于猜测的、实际不需要的功能。如果不清楚一个系统是否需要命令模式,一般就不要着急去实现它,事实上,在需要的时候通过重构来实现这个模式并不难,只有在真正需要上述场景中的功能时,把原来的代码重构为命令模式才有意义。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值