命令模式,这个模式从名字上看就很简单,命令嘛,老大发布命令,小兵执行就行咯。
以项目组为例子来讲命令模式
项目组的成员分工也是采用了常规的分工方式,分为需求组、美工组、代码组。现在有客户要我们做一个项目,客户和需求组讨论需求,和美工组讨论页面,和代码组讨论实现,告诉他们修改这里,删除这里,增加这些等等。
我们把这个模式用类图表示一下:
这个类图很简单,客户和三个组都有交流,这也合情合理,那我们看看这个的实现,首先看抽象类,我们是面向接口或抽象类编程的嘛:
package com.example.xpeng.myapplication;
/**
* Created by xpeng on 2018/6/2.
* 项目组分成了三个组,每个组还是要接受增删的命令
*/
public abstract class Group {
//甲乙双方分开办公,你要和那个组讨论,你首先要找到这个组
public abstract void find();
//被要求增加功能
public abstract void add();
//被要求删除功能
public abstract void delete();
//被要求修改功能
public abstract void change();
//被要求给出所有的变更计划
public abstract void plan();
//撤销计划
public abstract void undo();
}
大家看抽象类中的两个方法,是不是每个都是一个命令?找到它,增加,删除,给我计划!是不是命令,这也就是命令模式中的命令接受角色,等会细讲,我们再看三个实现类,需求组最重要,没有需求组你还设计个屁啊。看RequirmentGroup类的实现:
package com.example.xpeng.myapplication;
import android.util.Log;
/**
* Created by xpeng on 2018/6/2.
* 需求组的职责是客户谈定需求,这个组的人应该都是业务领域专家
*/
public class RequirementGroup extends Group {
//客户要求需求组过去和他们谈
@Override
public void find() {
Log.e("xyz","找到需求组...");
}
@Override
public void add() {
Log.e("xyz","客户要求添加一项需求...");
}
@Override
public void delete() {
Log.e("xyz","客户要求删除一项需求...");
}
@Override
public void change() {
Log.e("xyz","客户要求修改一项需求...");
}
@Override
public void plan() {
Log.e("xyz","客户要求变更计划...");
}
@Override
public void undo() {
Log.e("xyz","客户要求撤销计划...");
}
}
需求组有了,我们再看美工组
package com.example.xpeng.myapplication;
import android.util.Log;
/**
* Created by xpeng on 2018/6/2.
* 美工组
*/
public class PageGroup extends Group {
//客户要求需求组过去和他们谈
@Override
public void find() {
Log.e("xyz","找到美工组...");
}
@Override
public void add() {
Log.e("xyz","客户要求添加一个页面...");
}
@Override
public void delete() {
Log.e("xyz","客户要求删除一个页面...");
}
@Override
public void change() {
Log.e("xyz","客户要求修改一个页面...");
}
@Override
public void plan() {
Log.e("xyz","客户要求页面变更计划...");
}
@Override
public void undo() {
Log.e("xyz","客户要求页面撤销计划...");
}
}
最后再看代码组:
package com.example.xpeng.myapplication;
import android.util.Log;
/**
* Created by xpeng on 2018/6/2.
* 代码组
*/
public class CodeGroup extends Group {
//客户要求需求组过去和他们谈
@Override
public void find() {
Log.e("xyz","找到代码组...");
}
@Override
public void add() {
Log.e("xyz","客户要求添加一个功能...");
}
@Override
public void delete() {
Log.e("xyz","客户要求删除一个功能...");
}
@Override
public void change() {
Log.e("xyz","客户要求修改一个功能...");
}
@Override
public void plan() {
Log.e("xyz","客户要求代码变更计划...");
}
@Override
public void undo() {
Log.e("xyz","客户要求代码撤销计划...");
}
}
整个项目组的三个支柱都已经产生了,那看客户怎么和我们谈。客户刚开始給了一份他们自己写的需求,还是比较完整的,需求组根据这份需求写了一份分析说明书,客户一看,不对,要增加点需求,看程序:
private void client() {
//首先客户找到需求组说,过来谈需求,并修改
Log.e("xyz","客户要求增加一个需求");
Group rg = new RequirementGroup();
rg.find();
rg.add();
rg.plan();
}
好的,客户的需求达到了,需求刚开始没考虑周全,增加需求在所难免,理解理解。然后过了一段时间,客户说“界面多画了一个,过来谈谈”,于是L:
private void client() {
//首先客户找到美工组说,要修改
Goup rg = new PageGroup();
rg.find();
rg.add();
rg.plan();
}
这个页面改了之后,后面又有各种需要修改的地方每次都找页面组,需求组,美工组等等,烦了,后来一想,应该找一个接头人啊,客户有什么更改找接头人就行,先看类图:
类图中增加了不少,看着也比较清晰,比较简单的(Command抽象类与Group抽象类是没有关联关系的,与Group的三个实现类是由关联关系,为了线条不交叉就直接画上父类有关系),增加了几个类说明如下:
Command抽象类:客户发给我们的命令,定义三个工作组的成员变量,供子类使用:定义一个抽象方法execute,由子类来实现:
Invoke实现类:项目接头人,setComand接受客户发给我们的命令,action方法是执行客户命令
我们先看Command抽象类代码:
package com.example.xpeng.myapplication;
/**
* Created by xpeng on 2018/6/2.
* 命令的抽象类,我们把客户发出的命令定义成一个一个的对象
*/
public abstract class Command {
//把三个组都定义好,子类可以直接使用
protected RequirementGroup rg = new RequirementGroup();//需求组
protected PageGroup pg = new PageGroup();//美工组
protected CodeGroup cg = new CodeGroup();//需求组
//只要一个方法,你要我做什么事情\
public abstract void execute();
}
这个简单,看两个具体实现类,先看AddRequirementCommand类,这个类的作用就是增加一项需求。
package com.example.xpeng.myapplication;
/**
* Created by xpeng on 2018/6/2.
* 添加一项需求
*/
public class AddRequirementCommand extends Command {
//执行增加一项需求的命令
@Override
public void execute() {
//找到需求组
super.rg.find();
//增加一份需求
super.rg.add();
//页面也要增加
super.pg.add();
//功能也要增加
super.cg.add();
//给出计划
super.rg.plan();
}
}
看删除一个页面的命令,DeletePageCommand类
package com.example.xpeng.myapplication;
/**
* Created by xpeng on 2018/6/2.
* 添加一项需求
*/
public class DeletePageCommand extends Command {
//执行删除一个页面的的命令
@Override
public void execute() {
//找到页面组
super.pg.find();
//删除一个页面
super.pg.delete();
//给出计划
super.pg.plan();
}
}
Command抽象类还可以有很多的子类,比如增加一个功能命令(AddCodeCommand),删除一份需求命令(DeleteRequirementCommand)等等,这里就不用描述了,都很简单。
我们再来看看我们的接头人,就是Invoker类的实现:
package com.example.xpeng.myapplication;
/**
* Created by xpeng on 2018/6/2.
* 接头人的职责就是接受命令,并执行
*/
public class Invoker {
//什么命令
private Command command;
//客户发出命令
public void setCommand(Command command){
this.command = command;
}
//执行客户的命令
public void action(){
this.command.execute();
}
}
最后再来看实现:
private void client() {
//首先客户找到需求组说,过来谈需求,并修改
Log.e("xyz","客户要求增加一个需求");
//
// Group rg = new RequirementGroup();
// rg.find();
// rg.add();
// rg.plan();
//定义我们的接头人
Invoker invoker = new Invoker();
//客户要求增加一项命令
// Command command = new AddRequirementCommand();
//客户给我们下命令来
Command command = new DeletePageCommand();
invoker.setCommand(command);
invoker.action();
}
接下来看看命令模式的通用类图如下:
在这个类图中,我们看到三个角色:
Receiver角色:这个就是干活的角色,命令传递到这里是应该被执行的,具体到上面我们的例子中就是Group的三个实现类:
Command角色:就是命令,需要我执行的所有命令都在这里声明
Invoker角色:调用者,接收命令,并执行命令,例子中我这个项目经理就是这个角色
命令模式比较简单,但是在项目使用是非常频繁的,封装性非常好,因为他把请求方和执行方分开了,扩展性也有很好的保障。但是,命令模式也是有缺点的,你看Command的子类没有,那个如果我要写下去的可不是几个,而是几十个,这个类的膨胀的非常多,这个就需要大家在项目中自己考虑使用了。
上面的例子还没有说完。我们想想,客户要求增加一项需求,是不是页面也要增加,同时功能呢个也要增加呢呗?如果不使用命令模式,客户就需要先找到需求组,然后找美工组,然后找代码组,这个客户会崩溃的,使用命令模式之后,客户只管发布命令模式,你增加一个需求,没问题,我内部调用三个组通力合作,然后反馈结果,这也是客户所需要的,那这个怎么去修改呢?想想,很简单,在AddRirementCommand类的execute方法中增加对PageGroup和CodePage的调用就成了,修改后的代码如下:
package com.example.xpeng.myapplication;
/**
* Created by xpeng on 2018/6/2.
* 添加一项需求
*/
public class AddRequirementCommand extends Command {
//执行增加一项需求的命令
@Override
public void execute() {
//找到需求组
super.rg.find();
//增加一份需求
super.rg.add();
//页面也要增加
super.pg.add();
//功能也要增加
super.cg.add();
//给出计划
super.rg.plan();
}
}
看看是不是解决问题了?命令模式做了一层非常好的封装。那还有一个需求大家考虑:客户发出命令,那要是撤回怎么办呢?怎么实现呢,可以自己想想,很简单。
命令模式DEMO下载地址:
命令模式demo下载