文章参考:
汤高的Java设计模式(十八)—-命令模式
Java设计模式之命令模式
补充
刚开始对命令模式的 宏命令
撤销命令
命令队列
请求日志
不是很能理解,后面看了下面的老司机的博客,才感觉懂了一点点。
链接地址:
JsonShare猿的博客地址
命令模式基础篇 :
命令模式扩展篇 - 宏命令:
命令模式扩展篇 - 撤销命令:
命令模式扩展篇 - 命令队列:
命令模式扩展篇 - 请求日志:
顾名思义,命令模式一定是有命令发送者,命令接收者。命令发送者负责发送命令,命令接收者负责接收命令并完成具体的工作。在程序设计中,经常设计到一个对象需要请求另外一个对象调用其方法达到某种目的,如果请求着不希望或不直接和被请求者打交道,既请求者不包含被请求者的引 用,那么就可以使用命令模式。
结构
命令模式是对命令的封装。命令模式把发出命令的责任和执行命令的责任分割开,委派给不同的对象。
每一个命令都是一个操作:请求的一方发出请求要求执行一个操作;接收的一方收到请求,并执行操作。命令模式允许请求的一方和接收的一方独立开 来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否被执行、何时被执行,以及是怎么被执行的。
命令允许请求的一方和接收请求的一方能够独立演化,从而具有以下的优点:
1、命令模式使新的命令很容易地被加入到系统里。
2、能较容易地设计一个命令队列。
3、允许接收请求的一方决定是否要否决请求。 (暂时不理解)
4、可以容易地实现对请求的撤销和恢复。 (暂时不理解)
5、在需要的情况下,可以较容易地将命令记入日志。 (暂时不理解)
类图:
命令模式涉及到五个角色
命令(Command)角色:
声明了一个给所有具体命令类的抽象接口,规定了用来封装请求的若干个方法。
具体命令(ConcreteCommand)角色:
定义一个接收者和行为之间的弱耦合;实现execute()方法,负责调用接收者的相应操作。execute()方法通常叫做执行方法。
请求者(Invoker)角色:
具体命令的管理与维护类。请求者是一个包含”命令接口“变量的类的实例。请求者中的”命令“接口的变量可以存放任何具体命令的引用,请求者负责调用具体命令,让具体命令执行那些封装了请求的方法。
接收者(Receiver)角色:
负责具体实施和执行一个请求。任何一个类都可以成为接收者,实施和执行请求的方法叫做行动方法。
客户端(Client)角色:
创建一个具体命令(ConcreteCommand)对象并确定其接收者。
以部门分配任务的案例说明:
角色:
普通员工(命令的执行者)
经理(命令的传送者)
老板(命令的发起者)
定义命令接口
package com.madman.base.designMode.command;
/**
* 命令接口
* 定义一个命令
*/
public interface Command{
/**
* 定义命令
*/
void execute();
}
定义命令接收者
package com.madman.base.designMode.command;
/**
* 具体命令的接受者
* 被访问的对象
*/
public class Receiver{
public void writeCode() {
System.out.println("执行写代码的操作");
}
public void preparePPT() {
System.out.println("准备PPT");
}
}
命令的具体实现者
package com.madman.base.designMode.command;
/**
* 定义具体的命令...
* 用于管理被接受者
*/
public class CommandImpl implements Command{
Receiver r = new Receiver();
/**
* 定义命令
*/
@Override
public void execute() {
r.writeCode();
}
}
命令调用者
package com.madman.base.designMode.command;
/**
* 调用者
* 维护命令的类
*/
public class Invoker{
Command c;
public Command getC() {
return c;
}
/**
* 创建人:madman
* 创建时间:date()
*
* @param c
*/
public void setC(Command c) {
this.c = c;
}
public void action() {
c.execute();
}
}
客户端
package com.madman.base.designMode.command;
public class Client{
public static void main(String[] args) {
Command c = new CommandImpl();
Invoker in = new Invoker();
in.setC(c);
in.action();
}
}
执行结果
执行写代码的操作
宏命令
所谓宏命令简单点说就是包含多个命令的命令,是一个命令的组合。指挥官可以把一个一个的命令记录下来,再在任何需要的时候重新把这些记录下来的命令一次性执行,这就是所谓的宏命令集功能
在新增一个命令的具体实现类
package com.madman.base.designMode.command;
/**
* 定义具体的命令...
* 用于管理被接受者
*/
public class CommandImp2 implements Command{
Receiver r = new Receiver();
/**
* 定义命令
*/
@Override
public void execute() {
r.preparePPT();
}
}
新建一个Invoker2用来管理命令集合
package com.madman.base.designMode.command;
import java.util.ArrayList;
import java.util.List;
/**
* 调用者
* 维护命令的类
*/
public class Invoker2{
List<Command> commandList = new ArrayList<Command>();
public void add(Command c) {
commandList.add(c);
}
public void remove(Command c) {
commandList.remove(c);
}
public void action() {
for (Command command : commandList) {
command.execute();
}
}
}
客户端
package com.madman.base.designMode.command;
public class Client2{
public static void main(String[] args) {
Command c = new CommandImpl();
Command c2 = new CommandImp2();
Invoker2 in = new Invoker2();
in.add(c);
in.add(c2);
in.action();
System.out.println("-----------------");
in.remove(c);
in.action();
}
}
结果
执行写代码的操作
准备PPT
-----------------
准备PPT
命令模式优点
更松散的耦合
命令模式使得发起命令的对象——客户端,和具体实现命令的对象——接收者对象完全解耦,也就是说发起命令的对象完全不知道具体实现对象是谁,也不知道如何实现。
更动态的控制
命令模式把请求封装起来,可以动态地对它进行参数化、队列化和日志化等操作,从而使得系统更灵活。
很自然的复合命令
命令模式中的命令对象能够很容易地组合成复合命令,也就是宏命令,从而使系统操作更简单,功能更强大。
更好的扩展性
由于发起命令的对象和具体的实现完全解耦,因此扩展新的命令就很容易,只需要实现新的命令对象,然后在装配的时候,把具体的实现对象设置到命令对象中,然后就可以使用这个命令对象,已有的实现完全不用变化。
缺点
使用命令模式可能会导致某些系统有过多的具体命令类。因为针对每一个命令都需要设计一个具体命令类,因此某些系统可能需要大量具体命令类,这将影响命令模式的使用。
适用情况
1、系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。
2、系统需要在不同的时间指定请求、将请求排队和执行请求。
3、系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。(网络摘抄不理解…)
4、系统需要将一组操作组合在一起,即支持宏命令。