JAVA设计模式之命令者模式
在程序设计中,经常设计到一个对象需要请求另外一个对象调用其方法达到某种目的,如果请求这不希望或不直接和被请求者打交道,既请求者不包含被请求者的引用,那么就可以使用命令模式。例如在军队中,指挥官请求三连偷袭敌人,但是指挥官不希望或无法直接与三连取得联系,那么可以将该请求形成一个命令,该命令的核心是让三连偷袭敌人。只要能让该命令被执行,就会实现偷袭敌人的目的。
命令模式是关于怎样处理一个对象请求到另一个对象调用其方法完成某项任务的一种成熟的模式,这里称提出请求的对象为请求者,被请求的对象为接收者。在命令模式中,当一个对象请求另一个对象其调用方法时,不和被请求的对象直接打交道,而是把这这种请求封装到一个命令的对象中,起封装的手段是将请求封装在命令对象的一个方法中。命令模式的核心就是使用命令对象来封装方法调用。既请求者的请求,接受者调用方法。
结构:
命令者模式包括四种角色
接收者:接受者是一个类的实例,该实例负责执行与请求相关的操作
命令:命令是一个接口,规定了用来封装请求的若干个方法。
具体命令:具体命令是实现命令接口的类的实例,具体命令必须实现命令接口中的方法。
请求者:请求者是一个包含”命令接口“变量的类的实例。请求者中的”命令“接口的变量可以存放任何具体命令的引用,请求者负责调用具体命令,让具体命令执行那些封装了请求的方法
代码实例:
接受者
/**
* 接受者:具体执行命令的人
* @author hnylchf
*
*/
public class Receiver {
public void sendMessage(){
System.out.println("保证完成任务");
}
}
命令
package org.zhy.command;
/**
* 命令,封装请求的方法
* @author hnylchf
*
*/
public interface Command {
public void exceute();
}
请求者
package org.zhy.command;
/**
* 请求者
*
* @author hnylchf
*
*/
public class Invoker {
Command command;
public void startExceuteCommand(Command command) {
this.command = command;
command.exceute();
}
}
具体命令
package org.zhy.command;
/**
* 具体命令
*
* @author hnylchf
*
*/
public class ConcreteCommand {
Receiver receiver;
public ConcreteCommand(Receiver receiver) {
this.receiver = receiver;
}
public void execute() {
receiver.sendMessage();
}
}
演示:
package org.zhy.command;
public class Appliction {
public static void main(String[] args) {
Receiver receiver = new Receiver(); //执行者
Command command = new ConcreteCommand(receiver); //命令和具体命令
Invoker invoker = new Invoker(); //请求者
invoker.startExceuteCommand(command); //请求者下达命令
}
}
命令接口的撤销方法
命令接口中规定了用来封装请求的方法比如上例的execute方法,命令接口中还可以提供用来封装撤销请求的方法。既撤销方法的执行能撤销execute方法的执行效果。如果execute方法的执行效果不可撤销,那么具体命令就不必实现撤销方法。
以下示例一个关于撤销方法的demo
命令:
package org.zhy.commandundo;
public interface Command {
public void execute(String name);
public void undo();
}
具体命令:
package org.zhy.commandundo;
import java.util.ArrayList;
public class ConcreteCommand implements Command {
ArrayList dirNameList;
MakeDir dir;
public ConcreteCommand(MakeDir dir) {
dirNameList = new ArrayList();
this.dir = dir;
}
@Override
public void execute(String name) {
dir.createDir(name);
dirNameList.add(name); //记录添加到list中
}
@Override
public void undo() {
if (dirNameList != null && dirNameList.size() > 0) {
String str = dirNameList.get(dirNameList.size() - 1);
dir.deleteDir(str);
dirNameList.remove(dirNameList.size() - 1);
} else {
System.out.println("没有需要操作的记录");
}
}
}
执行者
package org.zhy.commandundo;
import java.io.File;
public class MakeDir {
public void createDir(String name) {
File file = new File(name);
file.mkdir();
}
public void deleteDir(String name) {
File file = new File(name);
file.delete();
}
}
请求者
package org.zhy.commandundo;
public class RequestMakedir {
Command command;
public RequestMakedir(Command command) {
this.command = command;
}
public void startExecuteCommand(String name) {
command.execute(name);
}
public void undoExecuteCommand() {
command.undo();
}
}
演示
package org.zhy.commandundo;
public class Appliction {
public static void main(String[] args) {
MakeDir dir = new MakeDir();
Command command = new ConcreteCommand(dir);
RequestMakedir requestMakedir = new RequestMakedir(command);
requestMakedir.startExecuteCommand("jiafei");
requestMakedir.startExecuteCommand("jiafei2");
requestMakedir.startExecuteCommand("jiafei3");
requestMakedir.undoExecuteCommand();
}
}
宏命令:
宏命令也是一个具体的命令,只不过他包含了其他命令的引用。当一个宏命令执行其execute方法时,将导致所引用的其他命令执行,因此执行一个宏命令相当于执行了多具体命令。
请求者输入英文字母或1-n之间的偶数可以请求既输出英文字母表,又输出1-n之间的偶数。
首先设计两个接收者,一个是输出英文字母表,一个是输出1-n之间的偶数
接收者1:打印英文字母
package org.zhy.commandmacro;
public class PrintLetter {
public void printEnglish() {
for (char c = 'a'; c < 'z'; c++) {
System.out.print(" " + c);
}
}
}接受者2:打印偶数
package org.zhy.commandmacro;
public class PrintNumber {
int n;
public PrintNumber(int n) {
this.n = n;
}
public void printEventNumber() {
for (int i = 0; i <= n; i++) {
if (i % 2 == 0) {
System.out.print(" " + i);
}
}
}
}
命令:
package org.zhy.commandmacro;
public interface Command {
public void execute();
}
命令1:处理打印字母
package org.zhy.commandmacro;
public class PrintEnglishCommand implements Command {
PrintLetter letter;
public PrintEnglishCommand(PrintLetter letter) {
this.letter = letter;
}
@Override
public void execute() {
letter.printEnglish();
}
}
命令2:处理打印偶数
package org.zhy.commandmacro;
public class PrintNumberCommand implements Command {
PrintNumber number;
public PrintNumberCommand(PrintNumber number) {
this.number = number;
}
@Override
public void execute() {
number.printEventNumber();
}
}
命令3:宏命令处理一批命令(打印字母和打印偶数)
package org.zhy.commandmacro;
import java.util.ArrayList;
public class MacroCommand implements Command {
ArrayList commandList;
public MacroCommand(ArrayList commandList) {
this.commandList = commandList;
}
@Override
public void execute() {
for (Command command : commandList) {
command.execute();
}
}
}
请求者
package org.zhy.commandmacro;
public class RequestPerson {
Command command;
public Command getCommand() {
return command;
}
public void setCommand(Command command) {
this.command = command;
}
public void startExecuteCommand() {
command.execute();
}
}
演示
package org.zhy.commandmacro;
import java.util.ArrayList;
public class Appliction {
public static void main(String[] args) {
ArrayList commandList = new ArrayList();
RequestPerson person = new RequestPerson();
Command englishCommand = new PrintEnglishCommand(new PrintLetter());
Command numberCommand = new PrintNumberCommand(new PrintNumber(10));
commandList.add(englishCommand);
commandList.add(numberCommand);
Command macroCommand = new MacroCommand(commandList);
person.setCommand(macroCommand);
person.startExecuteCommand();
}
}
命令者模式的优点 1、在命令者模式中,请求者不直接与接受者互交,既请求者不包含接受者的引用,因此彻底消除了彼此之间的耦合。 2、命令者模式满足了软件的“开-闭原则”。如果增加新的具体命令和该命令的接受者,不必修改调用者的代码,调用者就可以直接使用新的命令对象。反之如果增加新的调用者,不必修改现有的具体命令和接受者。新增加的调用者就可以使用已有的具体命令 3、由于请求者的请求被封装到了具体命令中,那么就可以将具体命令保存到持久化媒介中,在需要的时候重新执行这个具体命令。因此使用命令者模式可以记录日志 4、使用命令者模式可以对请求者的请求进行排队,每个请求者各自对应一个具体命令,因此可以按一定的顺序执行这些命令。 适用命令者模式的场景 1、程序需要在不同的时刻指定,排列和执行请求 2、程序需要提供撤销操作 3、程序需要支持宏操作