Command Pattern Written by Tianyapiao
1.定義
? ? ? ?將一個(gè)請(qǐng)求封裝為一個(gè)對(duì)象(即我們創(chuàng)建的Command對(duì)象)戒幔,從而使你可用不同的請(qǐng)求對(duì)客戶進(jìn)行參數(shù)化; 對(duì)請(qǐng)求排隊(duì)或記錄請(qǐng)求日志塘匣,以及支持可撤銷的操作好爬。命令模式是一種對(duì)象行為型模式,其別名為動(dòng)作(Action)模式或事物(Transaction)模式。
? ? ? ?相信大家看完命令模式定義的時(shí)候是一臉懵逼的,其實(shí)我也和你們一樣。但是我們?nèi)绻苈?lián)系下生活或者我們所學(xué)過(guò)的知識(shí)小腊,那么理解起來(lái)就應(yīng)該很容易了。下面就跟著我來(lái)探討一下命令模式吧久窟。
? ? ? ?學(xué)過(guò)Java的童鞋們應(yīng)該都配過(guò)jdk環(huán)境變量(jdk的環(huán)境配置在這里我們不做討論)秩冈,配置成功后,在cmd命令提示符中輸入java -version會(huì)彈出jdk的具體版本瘸羡,輸入javac則會(huì)彈出javac相關(guān)的命令操作漩仙,具體是什么我們來(lái)看圖:
? ? ? ?在這里我們可以將javac命令理解成一個(gè)請(qǐng)求的發(fā)送者,用戶通過(guò)它來(lái)發(fā)送一個(gè)“查看jdk環(huán)境是否配置成功的命令”犹赖,而jdk是請(qǐng)求的最終接收者和處理者,javac和java version "1.8.0_131"之間并不存在直接耦合的關(guān)系队他,它們通過(guò)JAVA_HOME,CLASSPATH,Path這三個(gè)環(huán)境變量的配置連接在一起,使得cmd最終輸出了java version "1.8.0_131"峻村,如果我們沒(méi)配置麸折,或者配置不成功,那么它們兩個(gè)完全沒(méi)有任何關(guān)系粘昨,cmd也會(huì)返回“javac不是內(nèi)部或外部命令”垢啼。
? ? ? ?說(shuō)了這么多 窜锯,我們來(lái)看看命令模式需要解決哪些問(wèn)題呢?請(qǐng)看下文:
2. 解決的問(wèn)題
? ? ? 在軟件系統(tǒng)中芭析,行為請(qǐng)求者與行為實(shí)現(xiàn)者通常是一種緊耦合的關(guān)系锚扎,但某些場(chǎng)合,比如需要對(duì)行為進(jìn)行記錄馁启、撤銷或重做驾孔、事務(wù)等處理時(shí),這種無(wú)法抵御變化的緊耦合的設(shè)計(jì)就不太合適惯疙。
3.命令模式的類圖
4.根據(jù)上面的類圖我們很容易得到以下代碼
//1.抽象命令類翠勉,用來(lái)聲明執(zhí)行操作的接口
public interface Command{
? ? ? ?void? execute();
}
//2.具體命令類,實(shí)現(xiàn)具體命令霉颠。
public class ConcereteCommand implements Command{
//具體命令類包含有一個(gè)接收者对碌,將這個(gè)接收者對(duì)象綁定于一個(gè)動(dòng)作
? ????? private Receiver receiver;
? ? ????public? ConcereteCommand(Receiver receiver){
? ? ? ? ? ?????this.receiver =receiver;
? ? ? ? }?????
? ? ?//說(shuō)這個(gè)實(shí)現(xiàn)是“虛”的,因?yàn)樗峭ㄟ^(guò)調(diào)用接收者相應(yīng)的操作來(lái)實(shí)現(xiàn)execute方法? ? ??
??????public void execute(){
? ? ? ????????? receiver.action();
? ? ????}
}
//3.請(qǐng)求接收者類蒿偎,知道如何實(shí)施與執(zhí)行一個(gè)請(qǐng)求相關(guān)的操作朽们,任何類都可能作為一個(gè)接收者。
public class Receiver{
????//真正的命令實(shí)現(xiàn)??
?????public void action(){
? ????????????System.out.println("Execute request!");
????????}
}
///請(qǐng)求調(diào)用者類酥郭,要求該命令執(zhí)行這個(gè)請(qǐng)求
public class Invoker{
????private Command command;
? ? // 設(shè)置命令????public void SetCommand(Command command){
????????this.command =command;
????}? ? ?//執(zhí)行命令? ????public void executeCommand(){
? ? ? ? command.execute();
?????}
}
//客戶端代碼
public class? Client{
????public static void main(string[] args)
????{
????????Receiver receiver=new Receiver();
? ? ? ? Command command=new ConcereteCommand(receiver);
????????Invoker invoker=new Invoker();
????????invoker.SetCommand(command);
????????invoker.executeCommand();
????}
}
在IntelliJ IDEA 2017.1 x64軟件中的測(cè)試結(jié)果如下:
5.應(yīng)用舉例
Sunny軟件公司開(kāi)發(fā)人員使用命令模式來(lái)設(shè)計(jì)“自定義功能鍵模塊”华坦,其核心結(jié)構(gòu)如下圖:
? ? ? 在圖中愿吹,F(xiàn)BSettingWindow是“功能鍵設(shè)置界面類”,FunctionButton充當(dāng)請(qǐng)求調(diào)用者不从,Command充當(dāng)抽象命令類,MinimizeCommand和HelpCommand充當(dāng)具體命令類犁跪,WindowHandler和HelpHandler充當(dāng)請(qǐng)求接收者椿息。完整代碼如下:
//1.抽象命令類
public abstract classCommand {
? ? ? ?public abstract voidexecute();
}
//2.具體命令類---幫助命令類
public class HelpCommand extends Command{
????private HelpHandler hhObj;//維持對(duì)請(qǐng)求接收者的引用
????public HelpCommand() {
????????hhObj=newHelpHandler();
????}
????//命令執(zhí)行方法,將調(diào)用請(qǐng)求接收者的業(yè)務(wù)方法
????@Override
????public void execute() {
????????hhObj.display();
????}
}
//3.具體命令類--最小化窗口命令類
public class MinimizeCommand extends Command{
????private WindowHandler whObj;//維持對(duì)請(qǐng)求接收者的引用
????public MinimizeCommand() {
????whObj=newWindowHandler();
}
????//執(zhí)行命令方法坷衍,將調(diào)用請(qǐng)求接收者的業(yè)務(wù)方法
????@Override
????public void execute() {
????????whObj.minimize();
????}
}
//4.窗口處理類:請(qǐng)求接收者
public class WindowHandler {
????public voidminimize(){
????????System.out.println("將窗口最小化至托盤(pán)寝优!");
????}
}
//5.幫助文檔處理類:請(qǐng)求接收者
public class HelpHandler {
????public void display(){
????????System.out.println("顯示幫助文檔!");
? ? ?}
}
//6..功能鍵設(shè)置窗口類
import java.util.ArrayList;
//功能鍵設(shè)置窗口類
public class FBSettingWindow{
????private String title;
????//定義一個(gè)ArrayList集合來(lái)存儲(chǔ)所有的功能鍵
????private ArrayList<FunctionButton>??fbs=newArrayList<>();
????public String getTitle() {
????????return title;
????}
????public void setTitle(String title) {
????????this.title= title;
????}
????public ArrayList<FunctionButton> getFb() {
????returnfbs;
????}
????public void setFb(ArrayList<FunctionButton> fbs) {
????????this.fbs= fbs;
????}
????public FBSettingWindow(String title) {
????????this.title= title;
????}
????public void addFunctionButton(FunctionButton fb){
????????fbs.add(fb);
????}
????public void removeFunctionButton(FunctionButton fb){
????????fbs.remove(fb);
????}
????//7.顯示窗口及功能鍵
????public void display(){
????????System.out.println("顯示窗口:"+title);
????????System.out.println("顯示功能鍵:");
????????for(Object obj:fbs){
????????????????System.out.println(((FunctionButton) obj).getName());
????????}
????????????????System.out.println("----------------------------");
????}
}
//8.功能鍵類:請(qǐng)求 的發(fā)送者
public class FunctionButton {
????private String name;
? ? private Command command;
????public FunctionButton(String name) {
????????this.name= name;
????}
????public String getName() {
? ????????return name;
? ? }
????//為功能鍵注入命令
? ? public void setCommand(Command command) {
????????this.command= command;
????}????
????//發(fā)送請(qǐng)求的方法
????public void onClick(){
????????System.out.print("點(diǎn)擊功能鍵:");
????????command.execute();
????}
}
//9.配置文件config.xml
<?xml version="1.0" encoding="UTF-8"?>
<config>
????<type>HelpCommand</type>
????<type>MinimizeCommand</type>
</config>
//10.xml配置文件解析類,此處我使用的是dom解析
注:使用下列代碼需要導(dǎo)入dom4j-1.6.1.jar?
下載地址? ?鏈接:http://pan.baidu.com/s/1geC6xMF? 密碼:e8mf
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.File;
import java.util.List;
public class XMLUtil{
public static Object getBean(inti){
try{
????//創(chuàng)建saxReader對(duì)象
????SAXReader reader=new SAXReader();
????//通過(guò)read方法讀取一個(gè)文件 轉(zhuǎn)換成Document對(duì)象
????Document doc=reader.read(newFile("src/config.xml"));
????//獲取根節(jié)點(diǎn)元素對(duì)象
????Element rootNode=doc.getRootElement();//得到了config
????String type =null;
????//獲取根元素節(jié)點(diǎn)下 所有元素的子節(jié)點(diǎn)
????List<Element> elements = rootNode.elements();
????if(0==i){
????????type= elements.get(0).getText();
????}else{
????????type= elements.get(1).getText();
????}
????Class c=Class.forName(type);
????Object obj=c.newInstance();
????return obj;
????}catch(Exception e) {
????????e.printStackTrace();
????????return null;
????????}
? ?}
}
//客戶端
public class Client {
????public static void main(String args[]){
????FBSettingWindow fbsw=new FBSettingWindow("功能鍵設(shè)置");
????FunctionButton fb1,fb2;
? ? fb1=new FunctionButton("功能鍵1");
????fb2=newFunctionButton("功能鍵2");
????Command cmd1,cmd2;
????//通過(guò)讀取配置文件和反射生成具體命令對(duì)象
????cmd1=(Command)XMLUtil.getBean(0);
????cmd2=(Command)XMLUtil.getBean(1);
????//將命令注入到功能鍵
????fb1.setCommand(cmd1);
????fb2.setCommand(cmd2);
????//Ctrl+D? 復(fù)制一行代碼(只適用于idea)
????fbsw.addFunctionButton(fb1);
????fbsw.addFunctionButton(fb2);
????fbsw.display();
????//調(diào)用功能鍵的業(yè)務(wù)方法
????fb1.onClick();
????fb2.onClick();
????}
}
//idea中的運(yùn)行結(jié)果和項(xiàng)目結(jié)構(gòu)圖如下:
6.適用場(chǎng)景
1.?命令的發(fā)送者和命令執(zhí)行者有不同的生命周期枫耳。命令發(fā)送了并不是立即執(zhí)行乏矾。
2. 命令需要進(jìn)行各種管理邏輯。
3.?需要支持撤消\重做操作(這種狀況的代碼大家可以上網(wǎng)搜索下迁杨,有很多钻心,這里不進(jìn)行詳細(xì)解讀)。
7.結(jié)論:
通過(guò)對(duì)上面的分析我們可以知道如下幾點(diǎn):
1. 命令模式是通過(guò)命令發(fā)送者和命令執(zhí)行者的解耦來(lái)完成對(duì)命令的具體控制的铅协。
2.?命令模式是對(duì)功能方法的抽象捷沸,并不是對(duì)對(duì)象的抽象。
3.?命令模式是將功能提升到對(duì)象來(lái)操作狐史,以便對(duì)多個(gè)功能進(jìn)行一系列的處理以及封裝痒给。
好了说墨,我對(duì)命令模式的理解到這里就結(jié)束了,如果大家發(fā)現(xiàn)有什么錯(cuò)誤的地方苍柏,希望能不吝指正尼斧。如果有疑問(wèn)的地方也可以提出,共同進(jìn)步试吁。
如果覺(jué)得文章對(duì)你有幫助突颊,請(qǐng)點(diǎn)個(gè)贊嘍,嘿嘿潘悼!
附:為了幫助大家更好的理解代碼律秃,我已經(jīng)把代碼上傳到github上,下面附上代碼鏈接