Android編程設(shè)計(jì)模式之命令模式詳解
本文實(shí)例講述了Android編程設(shè)計(jì)模式之命令模式。分享給大家供大家參考,具體如下:
一、介紹
命令模式(Command Pattern),是行為型設(shè)計(jì)模式之一。命令模式相對(duì)于其他的設(shè)計(jì)模式來(lái)說(shuō)并沒(méi)有那么多的條條框框,其實(shí)它不是一個(gè)很”規(guī)范“的模式,不過(guò),就是基于這一點(diǎn),命令模式相對(duì)于其他的設(shè)計(jì)模式更為靈活多變。我們接觸比較多的命令模式個(gè)例無(wú)非就是程序菜單命令,如在操作系統(tǒng)中,我們點(diǎn)擊”關(guān)機(jī)“命令,系統(tǒng)就會(huì)執(zhí)行一系列的操作,如先是暫停處理事件,保存系統(tǒng)的一些配置,然后結(jié)束程序進(jìn)程,最后調(diào)用內(nèi)核命令關(guān)閉計(jì)算機(jī)等,對(duì)于這一系列的命令,用戶不用去管,用戶只需點(diǎn)擊系統(tǒng)的關(guān)機(jī)按鈕即可完成如上一系列的命令。而我們的命令模式其實(shí)也與之相同,將一系列的方法調(diào)用封裝,用戶只需調(diào)用一個(gè)方法執(zhí)行,那么所有這些被封裝的方法就會(huì)被挨個(gè)執(zhí)行調(diào)用。
二、定義
將一個(gè)請(qǐng)求封裝成一個(gè)對(duì)象,從而讓用戶使用不同的請(qǐng)求把客戶端參數(shù)化;對(duì)請(qǐng)求排隊(duì)或者記錄請(qǐng)求日志,以及支持可撤銷(xiāo)的操作。
三、使用場(chǎng)景
需要抽象出待執(zhí)行的動(dòng)作,然后以參數(shù)的形式提供出來(lái)——類(lèi)似于過(guò)程設(shè)計(jì)中的回調(diào)機(jī)制,而命令模式正是回調(diào)機(jī)制的一個(gè)面向?qū)ο蟮奶娲贰?/p>
在不同的時(shí)刻指定、排列和執(zhí)行請(qǐng)求。一個(gè)命令對(duì)象可以有與初始請(qǐng)求無(wú)關(guān)的生存期。
需要支持取消操作。
支持修改日志功能,這樣當(dāng)系統(tǒng)崩潰時(shí),這些修改可以被重做一遍。
需要支持事務(wù)操作。
四、命令模式的UML類(lèi)圖
UML類(lèi)圖:
通用模式代碼:
接收者類(lèi):
public class Receiver { /* * 真正執(zhí)行具體命令邏輯的方法 */ public void action(){ System.out.println("具體執(zhí)行"); } }
抽象命令接口:
public interface Command { /* * 執(zhí)行具體操作的命令 */ void excute(); }
具體命令類(lèi):
public class ConcreteCommand implements Command { private Receiver receiver; public ConcreteCommand(Receiver receiver) { this.receiver = receiver; } @Override public void excute() { //調(diào)用接收者的相關(guān)方法來(lái)執(zhí)行具體邏輯 receiver.action(); } }
請(qǐng)求者類(lèi):
public class Invoker { private Command command; public Invoker(Command command) { this.command = command; } public void action(){ //調(diào)用具體命令對(duì)象的相關(guān)方法,執(zhí)行具體命令 command.excute(); } }
客戶類(lèi):
public class Client { public static void main(String[] args) { //構(gòu)造一個(gè)接收者對(duì)象 Receiver receiver = new Receiver(); //根據(jù)接收者對(duì)象構(gòu)造一個(gè)命令對(duì)象 Command command = new ConcreteCommand(receiver); //根據(jù)具體的對(duì)象構(gòu)造請(qǐng)求者對(duì)象 Invoker invoker = new Invoker(command); //執(zhí)行請(qǐng)求方法 invoker.action(); } }
角色介紹:
Receiver:接收者角色,該類(lèi)負(fù)責(zé)具體實(shí)施或執(zhí)行一個(gè)請(qǐng)求,說(shuō)得通俗點(diǎn)就是,執(zhí)行具體邏輯的角色,以本節(jié)開(kāi)頭的”關(guān)機(jī)“命令操作為例,其接收者角色就是真正執(zhí)行各項(xiàng)關(guān)機(jī)邏輯的底層代碼。任何一個(gè)類(lèi)都可以成為一個(gè)接收者,而在接收者類(lèi)中封裝具體操作邏輯的方法我們則稱為行動(dòng)方法。
Command:命令角色,定義所有具體命令類(lèi)的抽象接口。
ConcreteCommand:具體命令角色,該類(lèi)實(shí)現(xiàn)了Command接口,在execute方法中調(diào)用接收者角色的相關(guān)方法,在接收者和命令執(zhí)行的具體行為之間加以弱耦合。而execute則通常稱為執(zhí)行方法,如本文開(kāi)頭所述”關(guān)機(jī)“的操作實(shí)現(xiàn),具體可能還包含很多相關(guān)的操作,比如保存數(shù)據(jù)、關(guān)閉文件、結(jié)束進(jìn)程等,如果將這一系列具體的邏輯處理看作接收者,那么調(diào)用這些具體邏輯的方法就可以看作是執(zhí)行方法。
Invoker:請(qǐng)求者角色,該類(lèi)的職責(zé)就是調(diào)用命令對(duì)象執(zhí)行具體的請(qǐng)求,相關(guān)的方法我們稱為行動(dòng)方法,還是用”關(guān)機(jī)“為例,”關(guān)機(jī)“這個(gè)菜單命令一般就對(duì)應(yīng)一個(gè)關(guān)機(jī)方法,我們點(diǎn)擊了”關(guān)機(jī)“命令后,由這個(gè)關(guān)機(jī)方法去調(diào)用具體的命令執(zhí)行具體的邏輯,這里的”關(guān)機(jī)“對(duì)應(yīng)的這個(gè)方法就可以看作是請(qǐng)求者。
Client:客戶端類(lèi),Client可以創(chuàng)建具體的命令對(duì)象,并且設(shè)置命令對(duì)象的接收者。Tips:不能把Clinet理解為我們平常說(shuō)的客戶端,這里的Client是一個(gè)組裝命令對(duì)象和接受者對(duì)象的角色,或者你把它理解為一個(gè)裝配者。
五、簡(jiǎn)單實(shí)現(xiàn)
以推箱子游戲?yàn)槔?,一般游戲中?huì)有五個(gè)按鈕,分別是左移、右移、下移、上移和撤銷(xiāo)。那么玩游戲的人就是客戶端,五個(gè)按鈕就是調(diào)用者,執(zhí)行具體按鈕命令的方法是命令角色。
接收者角色:
public class PushBox { /** * 執(zhí)行向左命令 */ public void toLeft(){ System.out.println("向左"); } /** * 執(zhí)行向右命令 */ public void toRight(){ System.out.println("向右"); } /** * 執(zhí)行向下命令 */ public void toDown(){ System.out.println("向下"); } /** * 執(zhí)行向上命令 */ public void toUp(){ System.out.println("向上"); } /** * 執(zhí)行撤銷(xiāo)命令 */ public void revoke(){ System.out.println("撤銷(xiāo)"); } }
抽象命令接口:
public interface Command { /** * 命令執(zhí)行方法 */ void execute(); /** * 獲取命令類(lèi)型 */ void getCommand(); }
具體命令者,左移命令類(lèi):
public class LeftCommand implements Command{ //持有一個(gè)接受推箱子游戲?qū)ο蟮囊? private PushBox pushBox; public LeftCommand(PushBox pushBox){ this.pushBox = pushBox; } @Override public void execute() { //調(diào)用具體命令 pushBox.toLeft(); } @Override public void getCommand() { System.out.print("向左-->"); } }
具體命令者,右移命令類(lèi):
public class RightCommand implements Command{ //持有一個(gè)接受推箱子游戲?qū)ο蟮囊? private PushBox pushBox; public RightCommand(PushBox pushBox){ this.pushBox = pushBox; } @Override public void execute() { //調(diào)用具體命令 pushBox.toRight(); } @Override public void getCommand() { System.out.print("向右-->"); } }
具體命令者,上移命令類(lèi):
public class UpCommand implements Command{ //持有一個(gè)接受推箱子游戲?qū)ο蟮囊? private PushBox pushBox; public UpCommand(PushBox pushBox){ this.pushBox = pushBox; } @Override public void execute() { //調(diào)用具體命令 pushBox.toUp(); } @Override public void getCommand() { System.out.print("向上-->"); } }
具體命令者,下移命令類(lèi):
public class DownCommand implements Command{ //持有一個(gè)接受推箱子游戲?qū)ο蟮囊? private PushBox pushBox; public DownCommand(PushBox pushBox){ this.pushBox = pushBox; } @Override public void execute() { //調(diào)用具體命令 pushBox.toDown(); } @Override public void getCommand() { System.out.print("向下-->"); } }
具體命令者,撤銷(xiāo)命令類(lèi):
public class RevokeCommand implements Command{ //持有一個(gè)接受推箱子游戲?qū)ο蟮囊? private PushBox pushBox; public RevokeCommand(PushBox pushBox){ this.pushBox = pushBox; } @Override public void execute() { //調(diào)用具體命令 pushBox.revoke();; } @Override public void getCommand() { } }
請(qǐng)求者類(lèi),命令由按鈕發(fā)起:
public class Buttons { private LeftCommand leftCommand; //向左移動(dòng)的命令對(duì)象引用 private RightCommand rightCommand; //向右移動(dòng)的命令對(duì)象引用 private UpCommand upCommand; //向上移動(dòng)的命令對(duì)象引用 private DownCommand downCommand; //向下移動(dòng)的命令對(duì)象引用 private RevokeCommand revokeCommand; //撤銷(xiāo)命令對(duì)象引用 private ArrayList<Command> commandList = new ArrayList<Command>();//用于記錄命令動(dòng)作 /** * 獲取執(zhí)行命令 */ public void getCommandList(){ for(Command c : commandList){ c.getCommand(); } System.out.println(""); } /** * 設(shè)置向左移動(dòng)的命令對(duì)象 * * @param leftCommand 向左移動(dòng)的命令對(duì)象 */ public void setLeftCommand(LeftCommand leftCommand){ this.leftCommand = leftCommand; } /** * 設(shè)置向右移動(dòng)的命令對(duì)象 * * @param rightCommand 向右移動(dòng)的命令對(duì)象 */ public void setRightCommand(RightCommand rightCommand){ this.rightCommand = rightCommand; } /** * 設(shè)置向上移動(dòng)的命令對(duì)象 * * @param upCommand 向上移動(dòng)的命令對(duì)象 */ public void setUpCommand(UpCommand upCommand){ this.upCommand = upCommand; } /** * 設(shè)置向下移動(dòng)的命令對(duì)象 * * @param downCommand 向下移動(dòng)的命令對(duì)象 */ public void setDownCommand(DownCommand downCommand){ this.downCommand = downCommand; } /** * 設(shè)置撤銷(xiāo)命令對(duì)象 * * @param revokeCommand 撤銷(xiāo)命令對(duì)象 */ public void setRevokeCommand(RevokeCommand revokeCommand){ this.revokeCommand = revokeCommand; } /** * 按下向左按鈕 */ public void toLeft(){ leftCommand.execute(); commandList.add(leftCommand); } /** * 按下向右按鈕 */ public void toRight(){ rightCommand.execute(); commandList.add(rightCommand); } /** * 按下向上按鈕 */ public void toUp(){ upCommand.execute(); commandList.add(upCommand); } /** * 按下向下按鈕 */ public void toDown(){ downCommand.execute(); commandList.add(downCommand); } /** * 按下撤銷(xiāo)按鈕 */ public void toRevoke(){ revokeCommand.execute(); commandList.remove(commandList.size()-1); } }
客戶端調(diào)用:
public class Client { public static void main(String[] args) { //首先創(chuàng)建游戲 PushBox pushBox = new PushBox(); //根據(jù)游戲構(gòu)造5種命令 LeftCommand leftCommand = new LeftCommand(pushBox); RightCommand rightCommand = new RightCommand(pushBox); UpCommand upCommand = new UpCommand(pushBox); DownCommand downCommand = new DownCommand(pushBox); RevokeCommand revokeCommand = new RevokeCommand(pushBox); //按鈕可以執(zhí)行不同命令 Buttons buttons = new Buttons(); buttons.setLeftCommand(leftCommand); buttons.setRightCommand(rightCommand); buttons.setUpCommand(upCommand); buttons.setDownCommand(downCommand); buttons.setRevokeCommand(revokeCommand); //執(zhí)行操作 buttons.toLeft(); buttons.toDown(); buttons.toDown(); buttons.toRight(); buttons.getCommandList(); buttons.toRevoke(); buttons.toUp(); buttons.toLeft(); buttons.toDown(); buttons.toUp(); buttons.getCommandList(); } }
執(zhí)行結(jié)果:
向左 向下 向下 向右 向左-->向下-->向下-->向右--> 撤銷(xiāo) 向上 向左 向下 向上 向左-->向下-->向下-->向上-->向左-->向下-->向上-->
在這么長(zhǎng)的代碼之后是不是覺(jué)得很煩瑣,明明可以很簡(jiǎn)單的實(shí)現(xiàn),如下:
public class Client { public static void main(String[] args) { //首先創(chuàng)建游戲 PushBox pushBox = new PushBox(); pushBox.toDown(); pushBox.toRight(); pushBox.toUp(); } }
其實(shí)設(shè)計(jì)模式有一個(gè)重要的原則:對(duì)修改關(guān)閉對(duì)擴(kuò)展開(kāi)放。如果使用如上的簡(jiǎn)單方式,那么以后的修改只能去修改PushBox類(lèi),然后修改Client類(lèi),這顯然違反了這一原則。如果使用命令模式,那么Client類(lèi)無(wú)需修改,只需要修改PushBox類(lèi)的內(nèi)部操作,Client類(lèi)無(wú)需知道具體的內(nèi)部實(shí)現(xiàn)。
六、Android源碼中的命令模式
1、PackageHandler
PackageManagerService中,其對(duì)包的相關(guān)消息處理右其內(nèi)部類(lèi)PackageHandler承擔(dān),其將需要處理的請(qǐng)求作為對(duì)象通過(guò)消息傳遞給相關(guān)的方法,而對(duì)于包的安裝、移動(dòng)以及包大小的測(cè)量則分別封裝為HandlerParams的具體子類(lèi)InstallParams、MoveParams和MeasureParams。
源碼如下:
private abstract class HandlerParams { private static final int MAX_RETRIES = 4; /** * Number of times startCopy() has been attempted and had a non-fatal * error. */ private int mRetries = 0; final boolean startCopy() { boolean res; try { if (DEBUG_INSTALL) Slog.i(TAG, "startCopy"); if (++mRetries > MAX_RETRIES) { Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up"); mHandler.sendEmptyMessage(MCS_GIVE_UP); handleServiceError(); return false; } else { handleStartCopy(); res = true; } } catch (RemoteException e) { if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT"); mHandler.sendEmptyMessage(MCS_RECONNECT); res = false; } handleReturnCode(); return res; } final void serviceError() { if (DEBUG_INSTALL) Slog.i(TAG, "serviceError"); handleServiceError(); handleReturnCode(); } abstract void handleStartCopy() throws RemoteException; abstract void handleServiceError(); abstract void handleReturnCode(); }
可以看出HandlerParams也是一個(gè)抽象命令者。
七、總結(jié)
優(yōu)點(diǎn):
命令模式的封裝性很好,更弱的耦合性,更靈活的控制性以及更好的擴(kuò)展性。
缺點(diǎn):
類(lèi)的膨脹,大量衍生類(lèi)的創(chuàng)建。
更多關(guān)于Android相關(guān)內(nèi)容感興趣的讀者可查看本站專(zhuān)題:《Android開(kāi)發(fā)入門(mén)與進(jìn)階教程》、《Android調(diào)試技巧與常見(jiàn)問(wèn)題解決方法匯總》、《Android基本組件用法總結(jié)》、《Android視圖View技巧總結(jié)》、《Android布局layout技巧總結(jié)》及《Android控件用法總結(jié)》
希望本文所述對(duì)大家Android程序設(shè)計(jì)有所幫助。
相關(guān)文章
Android編程實(shí)現(xiàn)等比例顯示圖片的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)等比例顯示圖片的方法,實(shí)例分析了Android等比例縮放圖片的具體步驟與相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11flutter RotationTransition實(shí)現(xiàn)旋轉(zhuǎn)動(dòng)畫(huà)
這篇文章主要為大家詳細(xì)介紹了flutter RotationTransition實(shí)現(xiàn)旋轉(zhuǎn)動(dòng)畫(huà),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-07-07Android用過(guò)TextView實(shí)現(xiàn)跑馬燈效果的示例
本篇文章主要介紹了Android用過(guò)TextView實(shí)現(xiàn)跑馬燈效果的示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-08-08詳解Android 語(yǔ)音播報(bào)實(shí)現(xiàn)方案(無(wú)SDK)
本篇文章主要介紹了詳解Android 語(yǔ)音播報(bào)實(shí)現(xiàn)方案(無(wú)SDK),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-11-11Android實(shí)現(xiàn)捕獲未知異常并提交給服務(wù)器的方法
這篇文章主要介紹了Android實(shí)現(xiàn)捕獲未知異常并提交給服務(wù)器的方法,涉及Android的異常與錯(cuò)誤處理機(jī)制相關(guān)操作技巧,需要的朋友可以參考下2016-08-08Android 中的類(lèi)文件和類(lèi)加載器詳情
這篇文章主要介紹了Android 中的類(lèi)文件和類(lèi)加載器詳情,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-06-06Android 網(wǎng)絡(luò)圖片查看器與網(wǎng)頁(yè)源碼查看器
本篇文章主要介紹了Android 網(wǎng)絡(luò)圖片查看器與網(wǎng)頁(yè)源碼查看器的相關(guān)知識(shí)。具有很好的參考價(jià)值。下面跟著小編一起來(lái)看下吧2017-04-04Android中CheckBox復(fù)選框控件使用方法詳解
這篇文章主要為大家詳細(xì)介紹了Android中CheckBox復(fù)選框控件的使用方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08詳解Android中Intent傳遞對(duì)象給Activity的方法
這篇文章主要介紹了Android中Intent傳遞對(duì)象給Activity的方法,文章中對(duì)Activity的生命周期等知識(shí)先作了簡(jiǎn)要的介紹,需要的朋友可以參考下2016-04-04