命令模式包括四種角色:
接收者(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();
}
}
運行效果截圖: