深入淺出軟件設(shè)計(jì)模式之命令模式

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上,下面附上代碼鏈接

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末治唤,一起剝皮案震驚了整個(gè)濱河市棒动,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌宾添,老刑警劉巖船惨,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異缕陕,居然都是意外死亡粱锐,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)扛邑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)怜浅,“玉大人,你說(shuō)我怎么就攤上這事蔬崩《褡” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵沥阳,是天一觀的道長(zhǎng)跨琳。 經(jīng)常有香客問(wèn)我,道長(zhǎng)桐罕,這世上最難降的妖魔是什么脉让? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮功炮,結(jié)果婚禮上溅潜,老公的妹妹穿的比我還像新娘。我一直安慰自己死宣,他們只是感情好伟恶,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著毅该,像睡著了一般博秫。 火紅的嫁衣襯著肌膚如雪潦牛。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,784評(píng)論 1 290
  • 那天挡育,我揣著相機(jī)與錄音巴碗,去河邊找鬼。 笑死即寒,一個(gè)胖子當(dāng)著我的面吹牛橡淆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播母赵,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼逸爵,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了凹嘲?” 一聲冷哼從身側(cè)響起师倔,我...
    開(kāi)封第一講書(shū)人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎周蹭,沒(méi)想到半個(gè)月后趋艘,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡凶朗,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年瓷胧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片棚愤。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡搓萧,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出遇八,到底是詐尸還是另有隱情矛绘,我是刑警寧澤,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布刃永,位于F島的核電站,受9級(jí)特大地震影響羊精,放射性物質(zhì)發(fā)生泄漏斯够。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一喧锦、第九天 我趴在偏房一處隱蔽的房頂上張望读规。 院中可真熱鬧,春花似錦燃少、人聲如沸束亏。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)碍遍。三九已至定铜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間怕敬,已是汗流浹背揣炕。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留东跪,地道東北人畸陡。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像虽填,于是被迫代替她去往敵國(guó)和親丁恭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容