深入淺出軟件設計模式之命令模式

Command Pattern Written by Tianyapiao


1.定義

? ? ? ?將一個請求封裝為一個對象(即我們創(chuàng)建的Command對象),從而使你可用不同的請求對客戶進行參數化; 對請求排隊或記錄請求日志,以及支持可撤銷的操作。命令模式是一種對象行為型模式,其別名為動作(Action)模式或事物(Transaction)模式。

? ? ? ?相信大家看完命令模式定義的時候是一臉懵逼的,其實我也和你們一樣。但是我們如果能聯系下生活或者我們所學過的知識,那么理解起來就應該很容易了。下面就跟著我來探討一下命令模式吧。

? ? ? ?學過Java的童鞋們應該都配過jdk環(huán)境變量(jdk的環(huán)境配置在這里我們不做討論),配置成功后,在cmd命令提示符中輸入java -version會彈出jdk的具體版本,輸入javac則會彈出javac相關的命令操作,具體是什么我們來看圖:

? ? ? ?在這里我們可以將javac命令理解成一個請求的發(fā)送者,用戶通過它來發(fā)送一個“查看jdk環(huán)境是否配置成功的命令”,而jdk是請求的最終接收者和處理者,javac和java version "1.8.0_131"之間并不存在直接耦合的關系,它們通過JAVA_HOME,CLASSPATH,Path這三個環(huán)境變量的配置連接在一起,使得cmd最終輸出了java version "1.8.0_131",如果我們沒配置,或者配置不成功,那么它們兩個完全沒有任何關系,cmd也會返回“javac不是內部或外部命令”。

? ? ? ?說了這么多 ,我們來看看命令模式需要解決哪些問題呢?請看下文:

2. 解決的問題

? ? ? 在軟件系統中,行為請求者與行為實現者通常是一種緊耦合的關系,但某些場合,比如需要對行為進行記錄、撤銷或重做、事務等處理時,這種無法抵御變化的緊耦合的設計就不太合適。

3.命令模式的類圖

4.根據上面的類圖我們很容易得到以下代碼

//1.抽象命令類,用來聲明執(zhí)行操作的接口

public interface Command{

? ? ? ?void? execute();

}

//2.具體命令類,實現具體命令。

public class ConcereteCommand implements Command{

//具體命令類包含有一個接收者,將這個接收者對象綁定于一個動作

? ????? private Receiver receiver;

? ? ????public? ConcereteCommand(Receiver receiver){

? ? ? ? ? ?????this.receiver =receiver;

? ? ? ? }?????

? ? ?//說這個實現是“虛”的,因為它是通過調用接收者相應的操作來實現execute方法? ? ??

??????public void execute(){

? ? ? ????????? receiver.action();

? ? ????}

}

//3.請求接收者類,知道如何實施與執(zhí)行一個請求相關的操作,任何類都可能作為一個接收者。

public class Receiver{

????//真正的命令實現??

?????public void action(){

? ????????????System.out.println("Execute request!");

????????}

}

///請求調用者類,要求該命令執(zhí)行這個請求

public class Invoker{

????private Command command;

? ? // 設置命令????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軟件中的測試結果如下:

5.應用舉例

Sunny軟件公司開發(fā)人員使用命令模式來設計“自定義功能鍵模塊”,其核心結構如下圖:

? ? ? 在圖中,FBSettingWindow是“功能鍵設置界面類”,FunctionButton充當請求調用者,Command充當抽象命令類,MinimizeCommand和HelpCommand充當具體命令類,WindowHandler和HelpHandler充當請求接收者。完整代碼如下:

//1.抽象命令類

public abstract classCommand {

? ? ? ?public abstract voidexecute();

}

//2.具體命令類---幫助命令類

public class HelpCommand extends Command{

????private HelpHandler hhObj;//維持對請求接收者的引用

????public HelpCommand() {

????????hhObj=newHelpHandler();

????}

????//命令執(zhí)行方法,將調用請求接收者的業(yè)務方法

????@Override

????public void execute() {

????????hhObj.display();

????}

}

//3.具體命令類--最小化窗口命令類


public class MinimizeCommand extends Command{

????private WindowHandler whObj;//維持對請求接收者的引用

????public MinimizeCommand() {

????whObj=newWindowHandler();

}

????//執(zhí)行命令方法,將調用請求接收者的業(yè)務方法

????@Override

????public void execute() {

????????whObj.minimize();

????}

}

//4.窗口處理類:請求接收者

public class WindowHandler {

????public voidminimize(){

????????System.out.println("將窗口最小化至托盤!");

????}

}

//5.幫助文檔處理類:請求接收者

public class HelpHandler {

????public void display(){

????????System.out.println("顯示幫助文檔!");

? ? ?}

}

//6..功能鍵設置窗口類

import java.util.ArrayList;

//功能鍵設置窗口類

public class FBSettingWindow{

????private String title;

????//定義一個ArrayList集合來存儲所有的功能鍵

????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.功能鍵類:請求 的發(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ā)送請求的方法

????public void onClick(){

????????System.out.print("點擊功能鍵:");

????????command.execute();

????}

}

//9.配置文件config.xml

<?xml version="1.0" encoding="UTF-8"?>

<config>

????<type>HelpCommand</type>

????<type>MinimizeCommand</type>

</config>

//10.xml配置文件解析類,此處我使用的是dom解析

注:使用下列代碼需要導入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對象

????SAXReader reader=new SAXReader();

????//通過read方法讀取一個文件 轉換成Document對象

????Document doc=reader.read(newFile("src/config.xml"));

????//獲取根節(jié)點元素對象

????Element rootNode=doc.getRootElement();//得到了config

????String type =null;

????//獲取根元素節(jié)點下 所有元素的子節(jié)點

????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("功能鍵設置");

????FunctionButton fb1,fb2;

? ? fb1=new FunctionButton("功能鍵1");

????fb2=newFunctionButton("功能鍵2");

????Command cmd1,cmd2;

????//通過讀取配置文件和反射生成具體命令對象

????cmd1=(Command)XMLUtil.getBean(0);

????cmd2=(Command)XMLUtil.getBean(1);

????//將命令注入到功能鍵

????fb1.setCommand(cmd1);

????fb2.setCommand(cmd2);

????//Ctrl+D? 復制一行代碼(只適用于idea)

????fbsw.addFunctionButton(fb1);

????fbsw.addFunctionButton(fb2);

????fbsw.display();

????//調用功能鍵的業(yè)務方法

????fb1.onClick();

????fb2.onClick();

????}

}

//idea中的運行結果和項目結構圖如下:

6.適用場景

1.?命令的發(fā)送者和命令執(zhí)行者有不同的生命周期。命令發(fā)送了并不是立即執(zhí)行。

2. 命令需要進行各種管理邏輯。

3.?需要支持撤消\重做操作(這種狀況的代碼大家可以上網搜索下,有很多,這里不進行詳細解讀)。

7.結論:

通過對上面的分析我們可以知道如下幾點:

1. 命令模式是通過命令發(fā)送者和命令執(zhí)行者的解耦來完成對命令的具體控制的。

2.?命令模式是對功能方法的抽象,并不是對對象的抽象。

3.?命令模式是將功能提升到對象來操作,以便對多個功能進行一系列的處理以及封裝。

好了,我對命令模式的理解到這里就結束了,如果大家發(fā)現有什么錯誤的地方,希望能不吝指正。如果有疑問的地方也可以提出,共同進步。

如果覺得文章對你有幫助,請點個贊嘍,嘿嘿!

附:為了幫助大家更好的理解代碼,我已經把代碼上傳到github上,下面附上代碼鏈接

命令模式代碼 ? github.com/Tianyapiao/Command.git

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,622評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 99,716評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,746評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,991評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,706評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,036評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,029評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,203評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 49,725評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,451評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,677評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,161評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,857評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,266評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,606評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,407評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,643評論 2 380

推薦閱讀更多精彩內容