命令模式

命令模式

小故事:

经过多年的努力,终于我成为了一名项目经理,现在我要带着我的团队去接活了。项目员工分为 需求组(RequirementGroup,RG)、美工组(PageGroup,PG)、代码组(CodeGroup,CG)。刚开始,客户也就是甲方,很乐意和我们每个组探讨,比如和需求组讨论需求、和美工讨论页面、和代码组讨论实现,告诉他们增删改查的各种内容等。这是比较常见的甲乙方合作模式,甲方深入到乙方的项目开发中。可以用类图表示这个过程。

这里写图片描述

这个类图很简单,客户(client)和每个组都有交流,每个组都继承抽象类 Group

package com.abel.example;

/**
 * Created by yangyibo on 17/9/20.
 */
public abstract class Group {

    // 甲乙双方分开办公,如果要和某个组讨论问题,要先找到这个组
    public abstract void find();

    //被要求增加功能
    public abstract void add();

    //被要求删除功能
    public abstract void delete();

    //被要求改变功能
    public abstract void change();

    //被要求设计功能
    public abstract void plan();
}

可以看到抽象类中的每个方法,都是一个个的命令语气--“找到他,删除,增加,修改,给我执行计划!”都是一个个的命令,然后由相关的人员去执行。我门的三个实现类就是 执行人员。
代码如下:

/**
 * Created by yangyibo on 17/9/20.
 * 需求组
 */
public class RequirementGroup extends Group {

    //客户要求需求组过去和他们谈
    public void find() {
        System.out.println("找到需求组");
    }

    //客户需要添加一项需求
    public void add() {
        System.out.println("客户要添加一项需求");
    }
    //客户需要删除一项需求
    public void delete() {
        System.out.println("客户要删除一项需求");
    }
    //客户需要修改一项需求
    public void change() {
        System.out.println("客户要修改一项需求");
    }
    //客户要求给出变更计划
    public void plan() {
        System.out.println("客户要求给出变更计划");
    }
}

需求组有了,美工组也很重要,客户最终接触到的还是界面,美工组是脸面。

/**
 * Created by yangyibo on 17/9/20.
 * 美工组
 */
public class PageGroup extends Group {
    //客户要找到美工组过去和他们谈
    public void find() {
        System.out.println("找到美工组");
    }
    //美工被要求增加一个页面
    public void add() {
        System.out.println("美工被要求增加一个页面");
    }
    //美工被要求删除一个页面
    public void delete() {
        System.out.println("美工被要求删除一个页面");
    }
    //美工被要求修改一个页面
    public void change() {
        System.out.println("美工被要求修改一个页面");
    }
    //所有的增、删、改都要给出计划
    public void plan() {
        System.out.println("要求美工组所有的增、删、改都要给出计划");
    }
}

代码组

/**
 * Created by yangyibo on 17/10/17.
 * 码农组
 */
public class CodeGroup extends Group {
    //客户要求需求组过去和他们谈
    public void find() {
        System.out.println("找到代码组...");
    }
    //客户要求增加一项功能
    public void add() {
        System.out.println("客户要求增加一项功能");
    }
    //客户要求删除一项功能
    public void delete() {
        System.out.println("客户要求删除一项功能");
    }
    //客户要求修改一项功能
    public void change() {
        System.out.println("客户要求修改一项功能");
    }
    //客户要求给出变更计划
    public void plan() {
        System.out.println("客户要求给出变更计划");
    }
}

好了项目的组织结构都产生了,看客户怎么和我们谈。客户看了需求组写的分析说明书,要求增加需求,场景如下:

/**
 * Created by yangyibo on 17/10/17.
 * 场景类
 */
public class Client {
    public static void main(String[] args) {
        System.out.println("-----------客户要求增加一项需求---------------");
        Group rg = new RequirementGroup();
        //找到需求组
        rg.find();
        //添加一项需求
        rg.add();
        //给出变更计划
        rg.plan();

//客户的需求暂时满足了,过了一段时间客户要求新增一个界面。
        System.out.println("\n\n-----------过了一天客户要删除一个页面---------------");
        Group pg = new PageGroup();
        pg.find();
        pg.delete();
        pg.plan();
    }
}

好了需求改过了,页面也改过了,应该没什么问题了吧,过了一天后,客户又让代码组过去,说数据库设计问题,然后又叫美工组过去,布置一堆命令。。。。。问题来了,我门可以修改,但是每次都叫一个组过去,布置个任务,然后出计划,每次都这样。终于客户烦了,跟我说:“我不管你们内部怎么安排,你给我找个接头人,我只用给接头人发布命令就好了。”

我一听,好了,这个也正是我想要的。项目组的兄弟们也已经受不了了,于是我改变了一下我的处理方式。

这里写图片描述

在原有的类图上增加一个Invoker 类,用于根据命令,安排不同员工进行工作。但是客户的命令是一句话,是个String类型,这有非常多的变化,仅仅通过一个字符串来传递命令并不是一个非常好的方案,因为系统设计里字符串没有约束力。根据字符串判断业务逻辑不是一个优秀的解决方案。好的解决方案是:对客户发出的命令进行封装,每个命令是一个对象,避免 交流误差。封装后的结果就是,客户只用说一个命令,项目组就开始有条不紊的执行起来。如图

这里写图片描述

Command 抽象类客户发给我们的命令,只有一个execute方法,由子类实现。作用是执行命令,负责人Invoker 接收到命令后,立刻执行Command的execute方法。

/**
 * Created by yangyibo on 17/10/18.
 */
public abstract class Command {
    //把三个组都定义好,子类可以直接使用
    protected RequirementGroup rg = new RequirementGroup(); // 需求组
    protected PageGroup pg = new PageGroup(); //美工组
    protected CodeGroup cg = new CodeGroup(); // 代码组

    public abstract void execute();
}

增加需求的命令

public class AddRequirementCommand extends Command {

    public void execute() {
        //找到需求组
        super.rg.find();
        //添加需求
        super.rg.add();
        //给出计划
        super.rg.plan();
    }
}

删除页面的命令

public class DeletePageCommand extends Command {
    public void execute() {
        // 找到美工组
        super.pg.find();
        //删除一个页面
        super.pg.delete();
        // 给出计划
        super.pg.plan();

    }
}

command 抽象类可以有N个子类,如果增加一个功能命令,如增加或删除一个需求等,只要是客户产生且时常性的都可以定义为一个命令。

命令确定下来了,我门再看一下负责人Invoker
Invoker实现类,项目接头负责任,负责接收命令,action方法是执行客户的命令

public class Invoker {
    // 客户发送的命令
    private Command command;

    public Command getCommand() {
        return command;
    }

    public void setCommand(Command command) {
        this.command = command;
    }

    // 执行客户的命令
    public void action(){
        this.command.execute();
    }
}

好了改造完毕了,现在接到客户的命令,就立刻执行了,我门模拟增加一项需求的过程。

/**
 * Created by yangyibo on 17/10/18.
 */
public class Client {
    public static void main(String[] args) {
        // 定义接头人 sb
        Invoker sb = new Invoker();
        System.out.println("------------客户要求增加一个需求---------------");
        //客户下命令
        //删除页面命令
        Command command = new DeletePageCommand();
        //sb收到命令
        sb.setCommand(command);
        //sb 执行命令
        sb.action();
    }

运行结果

------------客户要求增加一个需求---------------
找到美工组
美工被要求删除一个页面
要求美工组所有的增、删、改都要给出计划

命令模式定义

命令模式是一个高内聚的模式,其定义为:将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对亲求排队或者纪录请求日志,可以提供命令的撤销和恢复功能。

Receive 接收者角色

该角色就是干活的角色,命令传递到这里是应该被执行的,具体到我们上面的小故事就是指需求组、代码组、美工组三个 Group 的实现类。

Command 命令角色

需要执行所有的命令都在这里声明。

Invoker调用者角色

收到命令,执行命令。在上面的小故事中,项目经理就是调用者。

命令模式比较简单,但是在项目中非常频繁的使用,因为它的封装性非常好,把请求方(Invoker)和执行方(Receiver)分开了,扩展性也有很好的保障,通用代码也比较简单。如下

这里写图片描述

通用Receiver类
public abstract class Receiver {
    // 抽象接受者,定义每个接收者都必须完成的业务
    public abstract void doSomething();
}

Receiver是一个抽象类,因为接收者可以有多个,多个就需要定义一个特性的抽象集合。

public class ConcreteReciver1 extends Receiver {
    //每个接收者都必须处理一定的业务逻辑
    public void doSomething() {
        System.out.println("执行者1 ,执行命令");
    }
}


public class ConcreteReciver2 extends Receiver{
    //每个接收者都必须处理一定的业务逻辑
    public void doSomething() {
        System.out.println("执行者2 ,执行命令");
    }
}

Receiver可以是N个,这要依赖业务定义,命令角色,是命令模式的核心。

Command
/**
 * Created by yangyibo on 17/10/18.
 */
public abstract class Command {
    // 每个命令类都必须有一个执行命令的方法
    public abstract void execute();
}

根据环境的需求,具体的命令类也有多个,读者可以再实际中扩展命令类,可以通过构造函数定义该命令是针对哪一个接收者。

public class ConcreteCommand1 extends Command {

    //对 receiver 类命令处理
    private Receiver receiver;

    //构造函数传递接收者
    public ConcreteCommand1(Receiver _receiver) {
        this.receiver = _receiver;
    }

    public void execute() {
        //业务处理
        this.receiver.doSomething();
        System.out.println("执行命令1");
    }
}

public class ConcreteCommand2 extends Command {

    //对 receiver 类命令处理
    private Receiver receiver;

    //构造函数传递接收者
    public ConcreteCommand2(Receiver _receiver) {
        this.receiver = _receiver;
    }

    public void execute() {
        //业务处理
        this.receiver.doSomething();
        System.out.println("执行命令2");
    }
}
调用者非常简单,只是实现命令的传递
public class Invoker {
    private Command command;

    public Command getCommand() {
        return command;
    }

    public void setCommand(Command command) {
        this.command = command;
    }

    public void action() {
        System.out.println("开始执行命令");
        this.command.execute();
        System.out.println("命令执行结束");

    }
}
场景类如下:
public class Client {
    public static void main(String[] args) {
        // 指定接头人
        Invoker invoker = new Invoker();
        // 指定执行者
        Receiver receiver = new ConcreteReciver1();
        // 发布命令
        Command command = new ConcreteCommand1(receiver);
        // 执行
        invoker.setCommand(command);
        invoker.action();
    }
}

命令模式的优点

类间解耦

调用者与接收者之间没有任何的依赖关系,调用者实现功能时只需嗲用Command的execute方法。不用了解时那个接收者执行。

可扩展性

Command的子类可以非常容易的扩展,而调用者Invoker和高层次的模块Client不产生严重代码耦合。

命令模式结合其他模式会更优秀

命令模式可以结合责任链模式,实现命令族解析任务,结合模版方法模式可以减少Command子类膨胀问题。

命令模式缺点

Command子类膨胀问题。如果有N个命令,就会有N个Command子类。

命令模式使用场景

只要认为是命令的地方就可以采用命令模式,例如模拟DOS命令的时候,触发反馈机制的处理等。

本文摘引自《设计模式之蝉(第二版)》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值