1.什么是命令模式?
将一个请求封装为一个对象,从而可用不同的请求对客户进行参数化,对请求排队或者记录请求日志,以及支持可撤销的操作。
说人话就是:请求发送者和接受者完全解耦,发送者只需要知道如何发送请求,不必指定如何完成请求。
举个例子:买点灯炮和开关,都是独立的,通过电线连起来
2.命令模式结构
(1)Command(抽象命令类):抽象命令类一般是一个抽象类或接口,在其中声明了用于执行请求的execute()等方法,通过这些方法可以调用请求接受者的相关操作。
(2)ConcreteCommand(具体命令类):具体命令类是抽象命令类的子类,实现了在抽象命令类中声明的方法,它对应具体的接受者对象,将接受者对象的动作绑定其中。具体命令类在实现execute()方法时将调用接受者对象的相关操作。
(3)Invoker(调用者):调用者即请求发送者,它通过命令对象来执行请求。一个调用者并不需要在设计师确定其接受者,因此它只与抽象命令类之间存在关联关系。在程序运行时可以将一个具体命令对象注入其中,在调用具体命令对象的execute()方法,从而实现简介调用请求接受者的相关操作。
(4)Receiver(接受者):接受者执行操作。
3.命令模式实现
(1)抽象命令类代码
/**
* 抽象命令类代码
* @author Administrator
*
*/
public abstract class Command {
public abstract void execute();
}
(2)调用者,它针对抽象命令类进行编程
/**
* 调用者,它针对抽象命令类进行编程
* @author Administrator
*
*/
public class Invoker {
private Command command;
//构造注入
public Invoker(Command command){
this.command = command;
}
//设值注入
public void setCommand(Command command){
this.command = command;
}
//业务方法,用于调用命令类的execute()方法
public void call(){
command.execute();
}
}
(3)请求接收者
/**
* 请求接收者
* @author Administrator
*
*/
public class Receiver {
public void action(){
//具体操作
}
}
(4)具体命令类
/**
* 具体命令类
* @author Administrator
*
*/
public class ConcreteCommand extends Command {
private Receiver receiver;//维持一个对象请求接受者对象的引用
@Override
public void execute() {
receiver.action();//调用请求接受者的业务处理方法action()
}
}
4.命令模式实例——功能键与对应功能实现解耦
(1)退出系统模拟实现类,充当请求接受者
/**
* 退出系统模拟实现类,充当请求接受者
* @author Administrator
*
*/
public class SystemExitClass {
public void exit(){
System.out.println("退出系统");
}
}
(2)显示帮助文档模拟实现类,充当请求接受者
/**
* 显示帮助文档模拟实现类,充当请求接受者
* @author Administrator
*
*/
public class DisplayHelpClass {
public void display() {
System.out.println("显示帮助文档");
}
}
(3)抽象命令类
/**
* 抽象命令类
* @author Administrator
*
*/
public abstract class Command {
public abstract void execute();
}
(4)功能键类,充当请求发送者
/**
* 功能键类,充当请求发送者
* @author Administrator
*
*/
public class FunctionButton {
private controller.commandModuleFunction.Command command;//维持一个抽象命令对象的引用
//设值注入
public void setCommand(controller.commandModuleFunction.Command command){
this.command = command;
}
//发送请求的方法
public void click(){
System.out.println("单击功能键");
command.execute();
}
}
(5)退出命令类,充当具体命令类
/**
* 退出命令类,充当具体命令类
* @author Administrator
*
*/
public class ExitCommand extends Command {
private SystemExitClass seObj;//维持对请求接受者的引用
public ExitCommand() {
seObj = new SystemExitClass();
}
//命令执行方法,将调用请求接受者的业务方法
@Override
public void execute() {
seObj.exit();
}
}
(6)帮助命令类,充当具体命令类
/**
* 帮助命令类,充当具体命令类
* @author Administrator
*
*/
public class HelpCommand extends Command {
private DisplayHelpClass hcObj;//维持对请求接受者的引用
public HelpCommand() {
hcObj = new DisplayHelpClass();
}
//命令执行方法,将调用请求接受者的业务方法
@Override
public void execute() {
hcObj.display();
}
}
(7)config.xml
<?xml version="1.0" encoding="UTF-8"?>
<config>
<className>controller.commandModuleFunction.ExitCommand</className>
</config>
(8)工具类
public class XMLUtil {
/**
* 从xml配置文件中提取具体类的类名,并返回一个实例对象
*/
public static Object getBean(){
try {
//创建DOM文档对象
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = documentBuilderFactory.newDocumentBuilder();
Document document = builder.parse(new File("src//controller//commandModuleFunction//config.xml"));
//获取包含类名的文本节点
NodeList nl = document.getElementsByTagName("className");
Node classNode = nl.item(0).getFirstChild();
String cName = classNode.getNodeValue();
//通过类名生成实例对象并将其返回
Class c = Class.forName(cName);
Object obj = c.newInstance();
return obj;
}catch (Exception e){
e.printStackTrace();
return null;
}
}
}
(9)客户端
public class Client {
public static void main(String[] args) {
FunctionButton fb = new FunctionButton();
Command command;//定义命令对象
command = (Command)XMLUtil.getBean();//读取配置文件,反射生成对象
fb.setCommand(command);//将命令对象注入功能键
fb.click();//调用功能键业务方法
}
}
(10)运行结果
5.实现命令队列
当一个请求发送者发送一个请求时有不止一个请求接受者产生响应,这些请求接受者将逐个执行业务方法,完成对请求的处理,此时,通过命令队列来实现。
(1)命令队列
public class CommandQueue {
//定义一个ArrayList来存储命令队列
private ArrayList<Command> commands = new ArrayList<Command>();
public void addCommand(Command command) {
commands.add(command);
}
public void removeCommand(Command command) {
commands.remove(command);
}
//循环调用每一个命令对象的execute()方法
public void execute() {
for(Object command:commands){
((Command)command).execute();
}
}
}
(2)发送者类
/**
* 发送者类
* @author Administrator
*
*/
public class Invoker {
private CommandQueue commandQueue;//维持一个CommandQueue对象的引用
//构造注入
public Invoker(CommandQueue commandQueue) {
this.commandQueue = commandQueue;
}
//设置注入
private void setCommandQueue(CommandQueue commandQueue) {
this.commandQueue = commandQueue;
}
//调用CommandQueue类的execu()方法
public void call() {
commandQueue.execute();
}
}
6.实现撤销操作——计数器进行数学运算,还可以对运算实施撤销操作
(1)加法类充当请求接受者
/**
* 加法类充当请求接受者
* @author Administrator
*
*/
public class Adder {
private int num = 0;//定义初始值为0
//加法操作,每次将传入的值与num做加法运算,再将结果返回
public int add(int value) {
num += value;
return num;
}
}
(2)抽象命令类
/**
* 抽象命令类
* @author Administrator
*
*/
public abstract class AbstractCommand {
public abstract int execute(int value);//声明命令执行方法
public abstract int undo();//声明撤销方法
}
(3)具体命令类
/**
* 具体命令类
* @author Administrator
*
*/
public class AddCommand extends AbstractCommand {
private Adder adder = new Adder();
private int value;
@Override
public int execute(int value) {
this.value = value;
return adder.add(value);
}
@Override
public int undo() {
return adder.add(-value);
}
}
(4)请求发送者
/**
* 请求发送者
* @author Administrator
*
*/
public class CalculatorForm {
private AbstractCommand command;
public void setCommand(AbstractCommand command){
this.command = command;
}
//调用命令对象的execute()方法执行运算
public void compute(int value){
int i = command.execute(value);
System.out.println("执行运算,结果为:"+i);
}
//调用命令对象的undo()方法进行撤销
public void undo() {
int i = command.undo();
System.out.println("执行撤销,运算结果为:"+i);
}
}
(5)客户端
public class Client {
public static void main(String[] args) {
CalculatorForm form = new CalculatorForm();
AbstractCommand command;
command = new AddCommand();
form.setCommand(command);//想发送者注入请求命令
form.compute(10);
form.compute(5);
form.compute(10);
form.undo();
}
}
(6)结果
7.宏命令
宏命令又叫组合命令,它是组合模式和命令模式联用的产物。宏命令不是一个具体命令类,它拥有一个集合,在该集合中包含了对其他命令对象的引用。
通常宏命令不直接与请求接受者交互,而是通过它的成员来调用请求接受者的方法。当调用宏命令的execut()方法时将递归调用它所包含的每个成员的execute()方法,一个宏命令的成员可以是简单命令,也可以继续是宏命令,执行一个宏命令将触发多个具体宏命令的执行,从而实现批处理
8.命令模式优缺点
优:
(1)请求者与接受者不存在直接引用,解耦
(2)新的命令可以很容易加到系统
(3)可以设计命令队列和宏命令
(4)为请求的撤销和恢复提供了一种设计实现方案
缺:
回导致过多的具体命令类
9.使用环境
(1)调用者和接受者需要解耦
(2)不同时间指定请求,将请求排队和执行请求
(3)命令的撤销和恢复
(4)组合操作