在软件的维护过程中,当客户需要添加新的功能求或其它的需要时,他们通常会说“叫你们的软件开发人帮我们做这么一个软件修改或设计”。软件公司则负责启动此次需求的执行。软件研发人员则根据具体的需求来进行回应。这就是一个命令模式在具体程序生活中的体现。我们将其抽象,可以分为五个角色:
客户角色 client 客户定制了新的需求,并确定新的需求的接收者。
软件公司 invoker 要求需求的实现,至于如何实现它无需关心。
程序研发人员 receiver 研发人员知道如何实现客户的需求。
一个新的需求 concretecommand 在多数情况下,客户的需求对于他自己来说是一个可以想像及描述的东西,也就是一个具体的存在。
需求:command 这是一个从具体的需求中抽象出来的概念,添加新的功能,修改软件和更改软件错误等各种需求都属于需求的一种。
命令模式让我们在设计GUI程序时变得非常的灵活,对于实现相同功能的按钮及菜单或弹出按钮只需共享此功能的一个实例既可。我们可以设计好界面用来响应用户的请求,我们可以往其上面添加各种具体的按钮,菜单,而其并不需要知道谁在响应,如何响应,只需响应用户的请求。
在GOF的<<Design Patterns>>中有对其的详细描述,在此,我们摘译如下:
意图:将一个请求封装成一个对象,从而使你可以使用不同的请求来初始化客户端对象,对请求对进排除和记录,同时支持撤消操作。
别名:Action(动作),Transaction(事务)。
参与角色:
Command 声明一个接口以用来实现某个操作。
ConcreteCommand 将动作与Reciver对外绑定,通过调用Reciver对象的相应方法来 实现Command的方法。
Client 创建ConcreteCommand对象,并设置其Reciver对象。
Invoker 要求该Command实现请求。
Reciver 知道如何实现具体的请求的类。
协作说明:
客户端创建了一个具体的Command对象并指定了其接收者。 调用者对象存储了此具体的Command对象。调用者对象通过执行Command对象的Execute方法来实现当前请求。如果命令是可以撤销时,具体对象在调用执行方法前将存储相应的状态以用来命令此请求。具体的Command对象调用其接收者的方法从而来实现相应请求。
命令模式有如下的一些好处:
1:命令模式将调用者对象与接收对象解耦(调用与实现解耦)。调用者实现功能时只需调用Command接口的Execute方法。
2:具体的Commands对象是第一层对象,它们可以像其他对象一样被扩展或继承。
3:你可以将多个Commands对象聚合成一个组合命令。组合命令也是组合对象模式的一个实例,将命令排队也是其的一种特殊情况。
4:你可以很容易的添加新的命令,因为你并不需要修改现有的代码。这也符合了开闭原则,对修改关闭,对扩展开放。
当然,任何东西都有其坏处的,如果我们对于所有命令都定义其command类时,那么在有些系统里将会产生commands类过多的情况,这样的问题在C++中可以使用模板来解决,当然使用模板的条件是commands类不需要参数且操作不能撤销的的。在使用命令模式的时候可能需要注意以下些地方:具体的Command类需要实现哪些功能?在支持撤销及重做的系统中,需要合理的保存状态。在多次撤销,重做操作后,注意可能的滞后影响所产生的程序错误。
你可以在以下需求的时候使用命令模式:
回调机制的面向对象设计需求。在设计GUI的按钮时,抽象出待执行的动作以参数化某对象。你可用过程语言中的回调(Callback)函数表达这种参数化机制。所谓回调函数是指函数先在某处注册,而它将在稍后某个需要的时候被调用。命令模式模式是回调机制的一个面向对象的替代品。
支持撤销以及重做操作。
记录日志修改从而可以在系统意外崩溃时再重做操作,通过在Command接口增加载入及记录日志操作,你可以保存一份持久完整的日志。从空难中恢复只需根据日志进行命令再次执行既可。
类似于SQL中事务回滚机制的需求。
最后三种需求基本上都是撤销与重做机制的各种变化。
最后让我们来看看其结构图及其时序图:(在此,我们还是借用gof里的结构图,当然其也有好多种变种,但只要体现了命令模式的几个特点就行-------面向接口,开闭原则实现与调用解耦)
在时序图中,我们简单的描述如下:某Invoker对象存储该ConcreteCommand对象。该Invoker通过调用Command对象的Execute操作来提交一个请求。若该命令是可撤消的,ConcreteCommand就在执行Excute操作之前存储当前状态以用于取消该命令。ConcreteCommand对象对调用它的Receiver的一些操作以执行该请求。 以下为开头这个例子的Java示例代码
/**
* 命令模式中Command接口
* Command.java
* @author Administrator
*
*/
public interface Command {
public void execute();
}
/**
* 命令模式中的具体命令类 Concrete Command
* ModifyCommand .java
* @author Administrator
*
*/
public class ModifyCommand implements Command {
Programer programer;
/**
* 需设计接收者 也就是命令的真正实现者
*
* @param programer
*/
public ModifyCommand(Programer programer) {
this.programer = programer;
}
public void execute() {
programer.coding();
}
}
/**
* 命令模式中的接收者类 Reciver
* Programer .java
* @author Administrator
*
*/
public class Programer {
/**
* 编程人员实现某项需求,是具体需求的真正执行者
*/
public void coding() {
System.out.println("design...coding...test...shell out");
}
}
/**
* 命令模式中的调用者Invoker
* Company .java
* @author Administrator
*
*/
public class Company {
private Command cmd;
public void add(Command cmd) {
this.cmd = cmd;
}
/**
* 对于命令模式而言,它只需知道启动命令的执行既可,与真正的实现者解耦 且此处使用的是Command接口
*/
public void action() {
cmd.execute();
}
}
/**
* 命令模式中的客户端 Client
* Client .java
* @author Administrator
*
*/
public class Client {
private ModifyCommand cmd;
/**
* 创建并设置其接收者
*
* @param programer
*/
public Client(Programer programer) {
cmd = new ModifyCommand(programer);
}
public Command getCmd() {
return cmd;
}
public void setCmd(ModifyCommand cmd) {
this.cmd = cmd;
}
}
//Demo
public class CommandDemo {
public static void main(String[] args) {
Client client = new Client(new Programer());
Company company = new Company();
company.add(client.getCmd());
company.action();
}
}
当然,我也有粗略的用此模式等其他的写了一个与Java用的小的写字板。以下仅为操作页面,实在是过于的简单,在此只show图片,有时间我再好好的完善下吧。