命令模式包括四种角色:
接收者(Receiver):接收者是一个类的实例,该实例负责执行与请求相关的操作。
命令(Command)接口:命令是一个接口,规定了用来封装请求的若干个方法。
具体命令(ConcreteCommand):具体命令是实现命令接口的实例。
请求者(Invoker):请求者是一个包含Command接口变量的类的实例。请求者中的Command接口的变量可以存放任何具体命令的引用。
UML类图:
简单实现代码:
package 命令模式;
/**
*
* @author 11659
* 2020年2月18日
*/
class Invoker {
Command command;
public void setCommand(Command command) {
this.command=command;
}
public Command getCommand() {
return command;
}
public void executeCommand() {
command.excute();
}
}
package 命令模式;
public interface Command {
abstract void excute();
}
package 命令模式;
public class ConcreteCommand implements Command {
Receiver receiver;
ConcreteCommand(Receiver receiver){
this.receiver=receiver;
}
@Override
public void excute() {
// TODO Auto-generated method stub
System.out.println("命令已传达");
receiver.action();
}
}
package 命令模式;
class Receiver {
public void action() {
System.out.println("命令已执行");
}
}
package 命令模式;
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
Receiver receiver=new Receiver();//创建接收者
Command command=new ConcreteCommand(receiver);//创建具体命令并指定接收者
Invoker invoker= new Invoker();//创建请求者
invoker.setCommand(command);
invoker.executeCommand();
}
}
运行:
命令模式的优点:
- 低耦合:即请求者不包含接收者的引用,不直接交互。
- 满足开-闭原则:如果增加具体命令和具体命令的接收者,不需要修改请求者,反之亦然。
- 由于请求者的请求被封装到了具体命令中,那么就可以将具体命令保存到持久化的媒介中。在需要的时候,可重新执行,或者撤销这个具体命令。
- 使用命令模式可以对请求者的请求进行排队。每个请求都各自对应一个具体命令,因此可按照顺序执行这些具体命令。
应用场景一:模拟日志记录,撤销操作
代码如下:
package 场景一撤销操作;
import java.io.*;
/**
*
* @author 11659
* 2020年2月18日
* 接受者
*/
public class MakeDir {
public void makeDir(String name) {
System.out.println("创建文件夹"+name);
File dir=new File(name);
dir.mkdir();
}
public void deleteDir(String name) {
System.out.println("删除文件夹"+name);
File dir=new File(name);
dir.delete();
}
}
package 场景一撤销操作;
public interface Command {
public abstract void execute(String name);
public abstract void undo();
}
package 场景一撤销操作;
import java.util.ArrayList;
public class ConcreteCommand implements Command{
ArrayList<String> dirNameList;
MakeDir makedir;
public ConcreteCommand(MakeDir makeDir) {
dirNameList=new ArrayList<String>();
this.makedir=makeDir;
// TODO Auto-generated constructor stub
}
@Override
public void execute(String name) {
// TODO Auto-generated method stub
makedir.makeDir(name);
dirNameList.add(name);
}
@Override
public void undo() {
// TODO Auto-generated method stub
if(dirNameList.size()>0) {
int m=dirNameList.size();
String str=dirNameList.get(m-1);
makedir.deleteDir(str);
dirNameList.remove(m-1);
}else {
System.out.println("没有需要撤销的操作");
}
}
}
package 场景一撤销操作;
public class RequestMakedir {
Command command;
public void SetCommand(Command command) {
this.command=command;
}
public void startExcuteCommand(String name) {
command.execute(name);
}
public void undoCommand() {
command.undo();
}
}
package 场景一撤销操作;
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建请求者
RequestMakedir rmd=new RequestMakedir();
//创建接收者
MakeDir md=new MakeDir();
//创建具体命令并指定接收者
Command command=new ConcreteCommand(md);
//为请求者注册具体命令
rmd.SetCommand(command);
//执行命令
rmd.startExcuteCommand("1");
rmd.startExcuteCommand("2");
rmd.startExcuteCommand("3");
//撤销命令
rmd.undoCommand();
rmd.undoCommand();
rmd.undoCommand();
rmd.undoCommand();
}
}
运行效果:
应用场景二:宏操作
请求者可以请求只输出英文字母表、俄文字母表或1~n之间的偶数也可以请求都输出。
代码实现:
package 场景二宏命令;
/**
*
* @author 11659
* 2020年2月18日
*/
public class PrintLetter {
public void printEnglish() {
for(char c='a';c<='z';c++) {
System.out.print(" "+c);
}
}
public void printRussian() {
for(char c='a';c<='я';c++) {
System.out.print(" "+c);
}
}
}
package 场景二宏命令;
public class PrintNumber {
int n;
PrintNumber(int n){
this.n=n;
}
public void printEvenNumber() {
for(int m=1;m<=n;m++) {
if(m%2==0) {
System.out.print(""+m);
}
}
}
}
package 场景二宏命令;
public class PrintEnglishCommand implements 场景二宏命令.Command {
PrintLetter letter;
public PrintEnglishCommand(PrintLetter letter) {
this.letter=letter;
// TODO Auto-generated constructor stub
}
@Override
public void execute() {
// TODO Auto-generated method stub
letter.printEnglish();
}
}
package 场景二宏命令;
public class PrintEvenNumber implements 场景二宏命令.Command {
PrintNumber printnumber;
public PrintEvenNumber(PrintNumber number) {
this.printnumber=number;
// TODO Auto-generated constructor stub
}
@Override
public void execute() {
// TODO Auto-generated method stub
printnumber.printEvenNumber();
}
}
package 场景二宏命令;
import java.util.ArrayList;
public class MacroCommand implements 场景二宏命令.Command {
ArrayList<场景二宏命令.Command> commandList;
MacroCommand(ArrayList<场景二宏命令.Command> list){
this.commandList=list;
}
@Override
public void execute() {
// TODO Auto-generated method stub
for(int i=0;i<commandList.size();i++) {
Command command=commandList.get(i);
command.execute();
}
}
}
package 场景二宏命令;
public class PrintRussianCommand implements Command{
PrintLetter printletter;
public PrintRussianCommand(PrintLetter letter) {
this.printletter=letter;
// TODO Auto-generated constructor stub
}
@Override
public void execute() {
// TODO Auto-generated method stub
printletter.printRussian();
}
}
package 场景二宏命令;
public class RequestPerson {
Command command;
public void setCommand(Command command) {
this.command=command;
}
public void startExcuteCommand() {
command.execute();
}
}
package 场景二宏命令;
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
//创建请求者
RequestPerson person =new RequestPerson();
//创建具体命令并指定接收者
Command command1=new PrintEnglishCommand(new PrintLetter());
Command command2=new PrintRussianCommand(new PrintLetter());
Command command3= new PrintEvenNumber(new PrintNumber(20));
ArrayList<Command> list= new ArrayList<Command>();
list.add(command1);
list.add(command2);
list.add(command3);
//创建宏命令
Command macroCommand=new MacroCommand(list);
System.out.println("单独输出英文字母表:");
person.setCommand(command1);
person.startExcuteCommand();
System.out.println();
System.out.println("宏命令执行");
person.setCommand(macroCommand);
person.startExcuteCommand();
}
}
运行效果截图: