设计模式简讲程序员

26. 命令模式

2018-07-12  本文已影响11人  Next_吴思成

定义

命令模式(Command Pattern):将一个请求封装为一个对象,从而让我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。其别名为动作(Action)模式或事务(Transaction)模式。

通俗理解

我们都有找过10086的客服,如果你的手机出什么问题了,都会拨打这个号码,请求客服的解决。通常请求客服的时候,你都会说明一下你的问题,是流量问题、还是话费问题,然后客服会根据你的这些问题,进行解决,然后你就很愉快地挂了电话。

实际上却没有这么简单,如果一个人打电话过来了,说他们家的小区出现了“乒乓效应”[1],老是断线。估计客服听到这个词都会一脸懵逼,不知道怎么处理吧。再或者,有一个人的问题是他的流量充值了,但是还没有到账,客服查明之后往这个人的账户上冲流量,结果一个手抖,多打了两个零,系统还不支持撤销,遇到这种情况又得扣工资。还有... ...

可见,客服也不容易。那么怎么样可以让客服更好地进行服务,减少犯错的几率呢?我们保持一个观点就好:专业的事情让专业的人去做。这时候,客服只是一个客服系统的门面,当客服接听到电话的时候,他会将问题转发给专业的人士,例如流量问题就让流量组的人去处理、基站问题就让设备维护人员去处理;第二个是,专业人士有相关的撤销方法,如果手抖了输多了一个零,可以撤销这个请求,中午还能加鸡腿🍗,当然,用户不满意的时候,还可以重复用户请求,把他的要求再做一遍。

这个就是命令模式。用户(Client)通过和客服(Invoker)交流,客服根据不同的问题,将这些问题转发到不同的组(Command),然后相应的组的成员去完成这个工作。

示例

客服系统作为示例。

渣渣程序

话费服务和流量服务

public class TrafficService {
    public void answer() {
        System.out.println("流量服务");
    }
}
public class PhoneChargeService {
    public void answer() {
        System.out.println("话费服务");
    }
}

客服

public class CustomerService {
    public void service(String type) {
        switch (type){
            case "phoneCharge":
                new PhoneChargeService().answer();
                break;
            case "traffic":
                new TrafficService().answer();
                break;
            default:
                System.out.println("服务不支持");
                break;
        }
    }
}

程序主入口

public class Main {
    public static void main(String[] args) {
        CustomerService service = new CustomerService();
        service.service("phoneCharge");
    }
}
//话费服务

缺点显而易见:

  1. 客服需要知道每一种问题的处理方法,即使TrafficService是其他客服,门面的客服也需要知道得调用TrafficService什么方法;
  2. 新增加处理类型需要修改CustomerService.service(String type)方法,违反开闭原则;
  3. 没有撤销。

优化

类图

image

程序

话费服务和流量服务

public class PhoneChargeService {
    public void answer() {
        System.out.println("话费服务");
    }
    public void undo() {
        System.out.println("撤回话费服务");
    }
}
//TrafficService类似,省略

命令接口和实现

public interface ICommand {
    /**
     * 执行
     */
    void execute();

    /**
     *  撤销
     */
    void undo();

    /**
     * 重做
     */
    void redo();
}
public class PhoneChangeCommand implements ICommand {
    private PhoneChargeService phoneChargeService;
    private boolean change;
    // 构造器,getter和setter方法
    public void execute() {
        phoneChargeService.answer();
        change = true;
    }
    public void undo() {
        if(change) {
            phoneChargeService.undo();
        } else {
            System.out.println("没有操作话费服务,撤销失败");
        }
    }
    public void redo() {
        execute();
    }
}
//TrafficCommand类似,省略

客服

public class Invoker {
    private List<ICommand> commands = new ArrayList<>();

    public List<ICommand> getCommands() {
        return commands;
    }

    public void setCommands(List<ICommand> commands) {
        this.commands = commands;
    }

    public void execumentCommand(ICommand command) {
        commands.add(command);
        command.execute();
    }

    public void redoCommand() {
        if(commands.size() > 0) {
            commands.get(commands.size()-1).redo();
            commands.add(commands.get(commands.size()-1));
        } else {
            System.out.println("任务列表当中无任务,不能撤销");
        }
    }

    public void undoCommand(){
        if(commands.size() > 0) {
            commands.get(commands.size()-1).undo();
            commands.remove(commands.size()-1);
        } else {
            System.out.println("没有服务可以撤销");
        }
    }
}

主入口

public class Main {
    public static void main(String[] args) {
        ICommand phoneCommand = new PhoneChangeCommand(new PhoneChargeService());
        ICommand trafficCommand = new TrafficCommand(new TrafficService());

        Invoker server = new Invoker();
        server.execumentCommand(phoneCommand);
        server.execumentCommand(trafficCommand);
        server.redoCommand();

        server.undoCommand();
        server.undoCommand();
        server.undoCommand();
        server.undoCommand();
    }
}
//话费服务
//流量服务
//流量服务
//撤回流量服务
//撤回流量服务
//撤回话费服务
//没有服务可以撤销

优点

  1. 如果需要添加一个新的服务类型,不需要修改任何的内容,只需要添加一个服务的处理类,一个命令实现类实现命令接口,并在这个命令实现类里面注入服务的处理类即可,符合开闭原则;
  2. 请求和接收解耦,Invoker通过ICommand调用PhoneChargeService,而Main则调动Invoker去调用服务方法;
  3. 容易设计出一个命令队列或者组合命令;
  4. 撤销undo和恢复redo。

缺点

  1. 类™的真多。

应用场景

  1. 请求与调用的解耦,就像10086的客服一样,我只需要知道我的问题解决了就可以,不需要知道这个过程,另外相关的维护组的人员也只需要知道有工作要做就行,不需要知道这个工作室谁派下来的;
  2. 需要支持撤销和恢复的操作;
  3. 需要将命令组合在一起。

实例

JDK Awt和Swing包,MenuItem类

代码

e26_command_pattern

https://www.jianshu.com/p/8be50cac2929


  1. 通信术语,指的是手机在两个基站覆盖范围的中间地带,手机不停切换对这两个基站的连接,造成信号的丢失,一般的解决办法是加大基站的功率,使得基站的覆盖范围更广,或者是增加基站的数量。

上一篇 下一篇

猜你喜欢

热点阅读