前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java设计模式之命令模式

Java设计模式之命令模式

作者头像
CoderJed
发布2018-11-09 16:20:31
3990
发布2018-11-09 16:20:31
举报
文章被收录于专栏:Jed的技术阶梯Jed的技术阶梯

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

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

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

代码语言:javascript
复制
/**
 * @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(); // 客户要求某小组列出执行计划

}
代码语言:javascript
复制
/**
 * @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("客户要求列出需求变更计划...");
    }

}
代码语言:javascript
复制
/**
 * @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("客户要求列出页面变更计划...");
    }

}
代码语言:javascript
复制
/**
 * @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("客户要求列出代码变更计划...");
    }

}

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

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

}

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

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

代码语言:javascript
复制
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()区分开,避免混淆)

修改后的代码如下:

代码语言:javascript
复制
public abstract class Command {
    
    protected RequirementGroup rg = new RequirementGroup();
    protected PageGroup pg = new PageGroup();
    protected CodeGroup cg = new CodeGroup();
    
    // 只要一个方法,你要我做什么事情
    public abstract void execute();

}

两个具体的实现类:

代码语言:javascript
复制
public class AddRequirementCommand extends Command {

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

}
代码语言:javascript
复制
public class DeletePageCommand extends Command {

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

}

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

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

代码语言:javascript
复制
public class Invoker {
    
    private Command command;

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

}

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

代码语言:javascript
复制
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的调用就成了,修改后的代码如下:

代码语言:javascript
复制
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()方法就可以加上这个撤销命令的功能,扩展起来还是很简单的。

本文原书: 《您的设计模式》 作者:CBF4LIFE

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018.10.22 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档