命令模式/Command
意图/适用场景:
命令模式是对命令的封装。命令模式把发出命令的责任和执行命令的责任分割开,委派给不同的对象。
每一个命令都是一个操作:请求的一方发出请求要求执行一个操作;接收的一方收到请求,并执行操作。命令模式允许请求的一方和接收的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是被谁处理、是否被处理、何时处理以及怎么处理。
命令模式有以下的优点:
- 允许请求方与接收方能够独立演化,使它们完全没有关系,只需要面向命令就可以了;
- 命令本身也容易演化,新的命令容易被加入系统;
- 接收请求的一方灵活性很大,可以选择接收与否,以及决定处理的细节;
- 容易设计一个命令队列;
- 可以容易地实现命令历史/日志功能。
UML:
参与者:
- 命令(Command):所有命令的抽象接口。
- 具体命令(ConcreteCommand):定义命令的具体内容。
- 递送者(Deliverer):负责命令的递送过程。它从请求者那里接收命令,并把命令分发到合适的地方去。这一角色的灵活性比较大,它可以维护一个命令队列,可以记录命令历史。
- 请求者(Invoker):命令的来源。
- 接收者(Receiver):所有命令处理器的抽象接口。
- 具体接收者(ConcreteReceiver):具体化Receiver接口,实现处理命令的具体功能。
要点:
这里描述的命令模式与《Java与模式》和《设计模式》中描述的命令者不大相同。
命令模式最关键的概念是把请求者、接收者以及命令三方解耦合,但这两本书中所定义的模式结构以及给出的示例代码不够好。
这两书中实例的好处在于从结构上把三方解耦,每一个角色都可以面向接口编程,在实现进有很大的选择空间。
但是,它们互相之间仍然互相引用,不能说是完全解耦。一个更好的方法是引入一个递送者角色,它负责命令的分发以及队列的维护。这样,三方者不需要再互相引用。而且在时序上看,可以做到异步递送,扩展了系统的功能。
应用实例:
这种命令/消息式的实例实在是太多。
还是看AWT的事件模式,在责任链模式里就讲过,一个事件产生后,会被依次分发给各个Listener去尝试处理,直到找到一个合适的处理者。
这里,可以想象有一个事件处理器,即Deliverer;产生事件的构件就是Invoker,它把事件交给Deliverer,就不再过问这个事件的处理过程了;Deliverer把事件交给第一个监听,也就是本模式中的Receiver,如果Receiver在handleEvent()中返回false,Deliverer还需要再找下一个Receiver。
示例代码:
[java]
// Source code from file:ConcreteReceiver.java
packagedesignPatterns.Command;
publicclass ConcreteReceiver implements Receiver {
publicvoid handle(Event e) {
System.out.println("ConcreteReceiver.handle()");
}
}
// Source code from file:Deliverer.java
packagedesignPatterns.Command;
publicclass Deliverer extends Thread {
// This simulate a event queue, the length of the queue is 1 here.
privateEvent queue = null;
privateReceiver receiver = null;
publicDeliverer(Receiver receiver) {
this.receiver= receiver;
}
publicvoid deliver(Event e) {
queue = e;
}
publicvoid distribute(Event e) {
receiver.handle(e);
}
publicvoid run() {
intcount = 3;
while(count-- > 0) {
try{
sleep(1000);
if(null != queue) {
distribute(queue);
queue =null;
}
}catch (Exception e) {}
}
}
}
// Source code from file:Event.java
packagedesignPatterns.Command;
publicclass Event {
inttype;
intparameterA;
intparameterB;
}
// Source code from file:Invoker.java
packagedesignPatterns.Command;
publicclass Invoker {
Deliverer deliverer =null;
publicInvoker(Deliverer deliverer) {
this.deliverer= deliverer;
}
publicvoid act() {
Evente = new Event();
deliverer.deliver(e);
}
}
// Source code from file:Receiver.java
packagedesignPatterns.Command;
publicinterface Receiver {
publicvoid handle(Event e);
}
// Source code from file:User.java
packagedesignPatterns.Command;
publicclass User {
publicstatic void main(String[] args) {
Receiverreceiver = new ConcreteReceiver();
Deliverer deliverer =new Deliverer(receiver);
deliverer.start();
Invoker invoker =new Invoker(deliverer);
invoker.act();
}
}
[/java]