设计模式——命令模式

假设某个项目组分为需求组(Requirement Group,简称RG)、美工组(Page Group,简称PG)、代码组(Code Group,简称CG),还有一个项目经理,刚开始的时候客户(甲方)很乐意和每个组探讨,比如和需求组讨论需求,和美工讨论页面,和代码组讨论实现,告诉他们修改这里,删除这里,增加这些等等,这是一种比较常见的甲乙方合作模式,甲方深入到乙方的项目开发中,我们把这个模式用类图表示一下:

这个类图很简单,客户和三个组都有交流,我们看看代码:

Group是命令模式中的命令接收者角色(Receiver),客户命令它增加功能、减少功能、列出计划等

/**
 * @description: 项目组分成了三个组,每个组要接受增删改的命令
 */
public abstract class Group {
    
    public abstract void add();
    public abstract void delete();
    public abstract void change();
    public abstract void find(); // 客户要和某个小组沟通,必须先找到对应的小组
    public abstract void plan(); // 客户要求某小组列出执行计划

}
/**
 * @description: 需求组
 */
public class RequirementGroup extends Group {

    @Override
    public void add() {
        System.out.println("客户要求增加一项需求...");
    }

    @Override
    public void delete() {
        System.out.println("客户要求删除一项需求...");
    }

    @Override
    public void change() {
        System.out.println("客户要求修改一项需求...");
    }

    @Override
    public void find() {
        System.out.println("找到需求组...");
    }

    @Override
    public void plan() {
        System.out.println("客户要求列出需求变更计划...");
    }

}
/**
 * @description: 美工组
 */
public class PageGroup extends Group {

    @Override
    public void add() {
        System.out.println("客户要求增加一个页面...");
    }

    @Override
    public void delete() {
        System.out.println("客户要求删除一个页面...");
    }

    @Override
    public void change() {
        System.out.println("客户要求修改一个页面...");
    }

    @Override
    public void find() {
        System.out.println("找到美工组...");
    }

    @Override
    public void plan() {
        System.out.println("客户要求列出页面变更计划...");
    }

}
/**
 * @description: 代码组
 */
public class CodeGroup extends Group {

    @Override
    public void add() {
        System.out.println("客户要求增加一个功能...");
    }

    @Override
    public void delete() {
        System.out.println("客户要求删除一个功能...");
    }

    @Override
    public void change() {
        System.out.println("客户要求修改某个功能...");
    }

    @Override
    public void find() {
        System.out.println("找到代码组...");
    }

    @Override
    public void plan() {
        System.out.println("客户要求列出代码变更计划...");
    }

}

假设客户要求增加一项需求,看代码怎么写:

public class Client {
    
    public static void main(String[] args) {
        System.out.println("----------------客户要求增加一项需求----------------");
        Group rg = new RequirementGroup();
        rg.find(); // 找到需求组
        rg.add(); // 增加需求
        rg.plan(); // 列出需求变更计划
    }

}

# 运行结果:
----------------客户要求增加一项需求----------------
找到需求组...
客户要求增加一项需求...
客户要求列出需求变更计划...

过了一段时间,客户发现有个页面太多余,要求删除这个页面,代码如下:

public class Client {
    
    public static void main(String[] args) {
        System.out.println("----------------客户要求删除一个页面----------------");
        Group pg = new PageGroup();
        pg.find(); // 找到美工组
        pg.delete(); // 删除某个页面
        pg.plan(); // 列出页面变更计划
    }

}

问题来了,修改是可以的,但是每次都是叫一个组去,布置个任务,然后出计划,次次都这样,如果让你当甲方也就是客户,你烦不烦?而且这种方式很容易出错误,而且还真发生过,客户把美工叫过去了,要删除,可美工说需求是这么写的,然后客户又命令需求组过去,一次次的折腾,客户也烦躁了,于是和项目经理说:“我不管你们内部怎么安排,你就给我找个接头人,我告诉他怎么做,删除页面了,增加功能了,你们内部怎么处理我不管,我就告诉他我要干什么就成了...”,项目经理也很乐意这么做,于是我们改变一下类图:

Command抽象类:客户发给我们的命令,定义三个工作组的成员变量,供子类使用;定义一个抽象方法execute(),由子类来实现;
Invoker实现类:项目接头人,setComand接受客户发给我们的命令,action()方法是执行客户的命令(方法名写成action()是与command的execute()区分开,避免混淆)

修改后的代码如下:

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 {

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

}
public class DeletePageCommand extends Command {

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

}

Command抽象类还可以有很多的子类,比如增加一个功能命令(AddCodeCommand),删除一份需求命令(DeleteRequirementCommand)等等,这里就不描述了,都很简单。

我们再看看接头人,就是Invoker类的实现:

public class Invoker {
    
    private Command command;

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

}

现在客户提出各种需求就简单的多了:

public class Client {
    
    public static void main(String[] args) {
        
        // 定义张三为接头人
        Invoker zhangsan = new Invoker();
        
        System.out.println("----------------客户要求增加一项需求----------------");
        // 客户下命令
        Command command = new AddRequirementCommand();
        // 接头人接受命令
        zhangsan.setCommand(command);
        // 接头人执行命令
        zhangsan.action();
    }
}

# 执行结果
----------------客户要求增加一项需求----------------
找到需求组...
客户要求增加一项需求...
客户要求列出需求变更计划...

假如客户又提出了新的需求:删除页面,那么只要在Client中把Command command = new AddRequirementCommand();换为Command command = new DeletePageCommand();即可,这就是命令模式,命令模式的通用类图如下:

在这个类图中,我们看到三个角色:

  • Receiver角色:这个就是干活的角色,命令传递到这里是应该被执行的,具体到上面我们的例子中就是Group的三个实现类;
  • Command角色:就是命令,需要执行的所有命令都这里声明;
  • Invoker角色:调用者,接收到命令,并执行命令,例子中项目经理就是这个角色;

命令模式比较简单,但是在项目中使用是非常频繁的,封装性非常好,因为它把请求方(Invoker)和执行方(Receiver)分开了,扩展性也有很好的保障。但是,命令模式也是有缺点的,如果需要扩展命令,就需要新增加Command类的子类,如果要新增的子类数量很多,那么是否使用命令模式就需要考虑了。

我们的例子还没有结束,通常来说,一个新增需求的命令,并不是通知给需求组就可以的,而是需要三个组通力合作的,那么我们在AddRequirementCommand命令中怎么调动其他组呢?很简单的,在AddRequirementCommand类的execute()方法中增加对PageGroup和CodePage的调用就成了,修改后的代码如下:

public class AddRequirementCommand extends Command {

    @Override
    public void execute() {
        // 找到需求组
        super.rg.find(); 
        // 增加一份需求
        super.rg.add(); 
        // 页面要增加
        super.pg.add();
        // 功能也要增加
        super.cg.add();
        //给出计划
        super.rg.plan();
    }

}

那么,客户提出需求后,又取消了,这个该怎么实现呢?其实取消也是一个命令,比如叫做undo(),那么在Group抽象类中增加undo()方法,3个Group继承类中重写undo()方法,然后在UndoRequirementCommand类中调用3个Group继承类中的undo()方法就可以加上这个撤销命令的功能,扩展起来还是很简单的。


原文:https://www.jianshu.com/p/2da80c468e0a

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值