Android設計模式之——命令模式

一、介紹

命令模式(Command Pattern),是行為型設計模式之一。命令模式相對于其他的設計模式來說并沒有那么多的條條框框,其實它不是一個很”規范“的模式,不過,就是基于這一點,命令模式相對于其他的設計模式更為靈活多變。我們接觸比較多的命令模式個例無非就是程序菜單命令,如在操作系統中,我們點擊”關機“命令,系統就會執行一系列的操作,如先是暫停處理事件,保存系統的一些配置,然后結束程序進程,最后調用內核命令關閉計算機等,對于這一系列的命令,用戶不用去管,用戶只需點擊系統的關機按鈕即可完成如上一系列的命令。而我們的命令模式其實也與之相同,將一系列的方法調用封裝,用戶只需調用一個方法執行,那么所有這些被封裝的方法就會被挨個執行調用。

二、定義

將一個請求封裝成一個對象,從而讓用戶使用不同的請求把客戶端參數化;對請求排隊或者記錄請求日志,以及支持可撤銷的操作。

三、使用場景

  • 需要抽象出待執行的動作,然后以參數的形式提供出來——類似于過程設計中的回調機制,而命令模式正是回調機制的一個面向對象的替代品。

  • 在不同的時刻指定、排列和執行請求。一個命令對象可以有與初始請求無關的生存期。

  • 需要支持取消操作。

  • 支持修改日志功能,這樣當系統崩潰時,這些修改可以被重做一遍。

  • 需要支持事務操作。

四、命令模式的UML類圖

UML類圖:

這里寫圖片描述

通用模式代碼:

接收者類:

public class Receiver {/** 真正執行具體命令邏輯的方法*/public void action(){System.out.println("具體執行");}
}

抽象命令接口:

public interface Command {/** 執行具體操作的命令*/void excute();
}

具體命令類:

public class ConcreteCommand implements Command {private Receiver receiver;public ConcreteCommand(Receiver receiver) {this.receiver = receiver;}@Overridepublic void excute() {//調用接收者的相關方法來執行具體邏輯receiver.action();}
}

請求者類:

public class Invoker {private Command command;public Invoker(Command command) {this.command = command;}public void action(){//調用具體命令對象的相關方法,執行具體命令command.excute();}
}

客戶類:

public class Client {public static void main(String[] args) {//構造一個接收者對象Receiver receiver = new Receiver();//根據接收者對象構造一個命令對象Command command = new ConcreteCommand(receiver);//根據具體的對象構造請求者對象Invoker invoker = new Invoker(command);//執行請求方法invoker.action();}
}

角色介紹:

  • Receiver:接收者角色,該類負責具體實施或執行一個請求,說得通俗點就是,執行具體邏輯的角色,以本節開頭的”關機“命令操作為例,其接收者角色就是真正執行各項關機邏輯的底層代碼。任何一個類都可以成為一個接收者,而在接收者類中封裝具體操作邏輯的方法我們則稱為行動方法。

  • Command:命令角色,定義所有具體命令類的抽象接口。

  • ConcreteCommand:具體命令角色,該類實現了Command接口,在execute方法中調用接收者角色的相關方法,在接收者和命令執行的具體行為之間加以弱耦合。而execute則通常稱為執行方法,如本文開頭所述”關機“的操作實現,具體可能還包含很多相關的操作,比如保存數據、關閉文件、結束進程等,如果將這一系列具體的邏輯處理看作接收者,那么調用這些具體邏輯的方法就可以看作是執行方法。

  • Invoker:請求者角色,該類的職責就是調用命令對象執行具體的請求,相關的方法我們稱為行動方法,還是用”關機“為例,”關機“這個菜單命令一般就對應一個關機方法,我們點擊了”關機“命令后,由這個關機方法去調用具體的命令執行具體的邏輯,這里的”關機“對應的這個方法就可以看作是請求者。

  • Client:客戶端類,Client可以創建具體的命令對象,并且設置命令對象的接收者。Tips:不能把Clinet理解為我們平常說的客戶端,這里的Client是一個組裝命令對象和接受者對象的角色,或者你把它理解為一個裝配者。

五、簡單實現

以推箱子游戲為例,一般游戲中會有五個按鈕,分別是左移、右移、下移、上移和撤銷。那么玩游戲的人就是客戶端,五個按鈕就是調用者,執行具體按鈕命令的方法是命令角色。

接收者角色:

public class PushBox {/*** 執行向左命令 */public void toLeft(){System.out.println("向左");}/*** 執行向右命令 */public void toRight(){System.out.println("向右");}/*** 執行向下命令 */public void toDown(){System.out.println("向下");}/*** 執行向上命令 */public void toUp(){System.out.println("向上");}/*** 執行撤銷命令 */public void revoke(){System.out.println("撤銷");}
}

抽象命令接口:

public interface Command {/*** 命令執行方法*/void execute();/*** 獲取命令類型*/void getCommand();
}

具體命令者,左移命令類:

public class LeftCommand implements Command{//持有一個接受推箱子游戲對象的引用private PushBox pushBox;public LeftCommand(PushBox pushBox){this.pushBox = pushBox;}@Overridepublic void execute() {//調用具體命令pushBox.toLeft();}@Overridepublic void getCommand() {System.out.print("向左-->");}
}

具體命令者,右移命令類:

public class RightCommand implements Command{//持有一個接受推箱子游戲對象的引用private PushBox pushBox;public RightCommand(PushBox pushBox){this.pushBox = pushBox;}@Overridepublic void execute() {//調用具體命令pushBox.toRight();}@Overridepublic void getCommand() {System.out.print("向右-->");}
}

具體命令者,上移命令類:

public class UpCommand implements Command{//持有一個接受推箱子游戲對象的引用private PushBox pushBox;public UpCommand(PushBox pushBox){this.pushBox = pushBox;}@Overridepublic void execute() {//調用具體命令pushBox.toUp();}@Overridepublic void getCommand() {System.out.print("向上-->");}
}

具體命令者,下移命令類:

public class DownCommand implements Command{//持有一個接受推箱子游戲對象的引用private PushBox pushBox;public DownCommand(PushBox pushBox){this.pushBox = pushBox;}@Overridepublic void execute() {//調用具體命令pushBox.toDown();}@Overridepublic void getCommand() {System.out.print("向下-->");}
}

具體命令者,撤銷命令類:

public class RevokeCommand implements Command{//持有一個接受推箱子游戲對象的引用private PushBox pushBox;public RevokeCommand(PushBox pushBox){this.pushBox = pushBox;}@Overridepublic void execute() {//調用具體命令pushBox.revoke();;}@Overridepublic void getCommand() {}
}

請求者類,命令由按鈕發起:

public class Buttons {private LeftCommand leftCommand; //向左移動的命令對象引用private RightCommand rightCommand; //向右移動的命令對象引用private UpCommand upCommand; //向上移動的命令對象引用private DownCommand downCommand; //向下移動的命令對象引用private RevokeCommand revokeCommand; //撤銷命令對象引用private ArrayList<Command> commandList = new ArrayList<Command>();//用于記錄命令動作/*** 獲取執行命令*/public void getCommandList(){for(Command c : commandList){c.getCommand();}System.out.println("");}/*** 設置向左移動的命令對象* * @param leftCommand 向左移動的命令對象*/public void setLeftCommand(LeftCommand leftCommand){this.leftCommand = leftCommand;}/*** 設置向右移動的命令對象* * @param rightCommand 向右移動的命令對象*/public void setRightCommand(RightCommand rightCommand){this.rightCommand = rightCommand;}/*** 設置向上移動的命令對象* * @param upCommand 向上移動的命令對象*/public void setUpCommand(UpCommand upCommand){this.upCommand = upCommand;}/*** 設置向下移動的命令對象* * @param downCommand 向下移動的命令對象*/public void setDownCommand(DownCommand downCommand){this.downCommand = downCommand;}/*** 設置撤銷命令對象* * @param revokeCommand 撤銷命令對象*/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);}/*** 按下撤銷按鈕 */public void toRevoke(){revokeCommand.execute();commandList.remove(commandList.size()-1);}
}

客戶端調用:

public class Client {public static void main(String[] args) {//首先創建游戲PushBox pushBox = new PushBox();//根據游戲構造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);//按鈕可以執行不同命令Buttons buttons = new Buttons();buttons.setLeftCommand(leftCommand);buttons.setRightCommand(rightCommand);buttons.setUpCommand(upCommand);buttons.setDownCommand(downCommand);buttons.setRevokeCommand(revokeCommand);//執行操作buttons.toLeft();buttons.toDown();buttons.toDown();buttons.toRight();buttons.getCommandList();buttons.toRevoke();buttons.toUp();buttons.toLeft();buttons.toDown();buttons.toUp();buttons.getCommandList();}
}

執行結果:

向左
向下
向下
向右
向左-->向下-->向下-->向右-->
撤銷
向上
向左
向下
向上
向左-->向下-->向下-->向上-->向左-->向下-->向上-->

在這么長的代碼之后是不是覺得很煩瑣,明明可以很簡單的實現,如下:

public class Client {public static void main(String[] args) {//首先創建游戲PushBox pushBox = new PushBox();pushBox.toDown();pushBox.toRight();pushBox.toUp();}
}

其實設計模式有一個重要的原則:對修改關閉對擴展開放。如果使用如上的簡單方式,那么以后的修改只能去修改PushBox類,然后修改Client類,這顯然違反了這一原則。如果使用命令模式,那么Client類無需修改,只需要修改PushBox類的內部操作,Client類無需知道具體的內部實現。

六、Android源碼中的命令模式

1、PackageHandler

PackageManagerService中,其對包的相關消息處理右其內部類PackageHandler承擔,其將需要處理的請求作為對象通過消息傳遞給相關的方法,而對于包的安裝、移動以及包大小的測量則分別封裝為HandlerParams的具體子類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也是一個抽象命令者。

七、總結

優點:

  • 命令模式的封裝性很好,更弱的耦合性,更靈活的控制性以及更好的擴展性。

缺點:

  • 類的膨脹,大量衍生類的創建。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/447012.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/447012.shtml
英文地址,請注明出處:http://en.pswp.cn/news/447012.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C++ 序列化和反序列化學習

定義 程序員在編寫應用程序的時候往往需要將程序的某些數據存儲在內存中&#xff0c;然后將其寫入某個文件或是將它傳輸到網絡中的另一臺計算機上以實現通訊。這些過程將會涉及到程序數據轉化成能被存儲并傳輸的格式&#xff0c;因此被稱為“序列化”&#xff08;Serializatio…

Android設計模式之——觀察者模式

一、介紹 觀察者模式是一個使用率非常高的模式&#xff0c;它最常用的地方是GUI系統、訂閱——發布系統。因為這個模式的一個重要作用就是解耦&#xff0c;將被觀察者和觀察者解耦&#xff0c;使得它們之間的依賴性更小&#xff0c;甚至做到毫無依賴。以GUI系統來說&#xff0…

Android設計模式之——備忘錄模式

一、介紹 備忘錄模式是一種行為模式&#xff0c;該模式用于保存對象當前狀態&#xff0c;并且在之后可以再次恢復到此狀態&#xff0c;這有點像我們平時說的”后悔藥“。備忘錄模式實現的方式需要保證被保存的對象狀態不能被對象從外部訪問&#xff0c;目的是為了保護好被保存…

c++ memory 頭文件詳細介紹

類 指針特征 pointer_traits (C11) 提供關于指針式類型的信息 (類模板) 垃圾收集器支持 pointer_safety (C11) 列出指針安全模式 (枚舉) 分配器 allocator 默認的分配器 (類模板) allocator_traits (C11) 提供關于分配器類型的信息 (類模板) allocator_arg_t (C11) 標簽類型…

C++ using的三種使用策略以及具體的用法

Using的使用方法 1&#xff0c;命名空間的使用 為了防止代碼沖突&#xff0c;都會使用到命名空間。假設這樣一種情況&#xff0c;當一個班上有兩個名叫 Zara 的學生時&#xff0c;為了明確區分他們&#xff0c;我們在使用名字之外&#xff0c;不得不使用一些額外的信息&#…

Android設計模式之——迭代器模式

一、介紹 迭代器模式&#xff08;Iterator Pattern&#xff09;又稱為游標&#xff08;Cursor&#xff09;模式&#xff0c;是行為型設計模式之一。迭代器模式算是一個比較古老的設計模式&#xff0c;其源于對容器的訪問&#xff0c;比如Java中的List、Map、數組等&#xff0c…

Android設計模式之——模板方法模式

一、介紹 在面向對象開發過程中&#xff0c;通常會遇到這樣的一個問題&#xff0c;我們知道一個算法所需的關鍵步驟&#xff0c;并確定了這些步驟的執行順序&#xff0c;但是&#xff0c;某些步驟的具體實現是未知的&#xff0c;或者說某些步驟的實現是會隨著環境的變化而改變…

Android設計模式之——訪問者模式

一、介紹 訪問者模式是一種將數據操作與數據結構分離的設計模式&#xff0c;它是《設計模式》中23種設計模式中最復雜的一個&#xff0c;但它的使用頻率并不高&#xff0c;正如《設計模式》的作者GOF對訪問者模式的描述&#xff1a;大多數情況下&#xff0c;你不需要使用訪問者…

C++類模板template <class T>簡單使用方法

一個簡單的例子 兩個數比大小 如果兩個數都是int類型 class Compare_int { public :Compare(int a,int b){xa;yb;}int max( ){return (x>y)?x:y;}int min( ){return (x<y)?x:y;} private :int x,y; }; 如果兩個數是float類型 class Compare_float { public :Compare(…

Android設計模式之——中介者模式

一、介紹 中介者模式&#xff08;Mediator Pattern&#xff09;也稱為調解者模式或調停者模式&#xff0c;Mediator本身就有調停者和調解者的意思。 在日常生活中調停者或調解者這個角色我們見得比較多的是“和事老”&#xff0c;也就是說調解兩個有爭端的人的角色&#xff0…

C++智能指針中unique_ptr部分內容的講解

參考鏈接 std::unique_ptr 介紹 定義位于頭文件<memory>std::unique_ptr 是通過指針占有并管理另一對象&#xff0c;并在 unique_ptr 離開作用域時釋放該對象的智能指針。 在下列兩者之一發生時用關聯的刪除器釋放對象&#xff1a;1&#xff0c;銷毀了管理的 unique_pt…

Java基礎——Java多線程中sleep()、wait()和notify()

一、sleep()sleep()方法源碼&#xff1a;/** * Causes the currently executing thread to sleep (temporarily cease * execution) for the specified number of milliseconds, subject to * the precision and accuracy of system timers and schedulers. The thread * does …

Key_handle的學習

代碼 一切盡在不言中 #pragma once#include "common/common.h" #include "sdf/sdf.h"#include <memory>namespace sdf {namespace algorithm {class KeyHandle {public:using erased_internal_data_t char; //使用erased_internal_data_t等效于ch…

Java基礎——虛擬機結構

一、Java平臺結構圖二、JVM、JRE和JDK關系JVM&#xff1a;Java Virtual Machine&#xff08;Java虛擬機&#xff09;&#xff0c;負責執行符合規范的Class文件 JRE&#xff1a; Java Runtime Environment &#xff08;java運行環境&#xff09;&#xff0c;包含JVM和類庫 JDK&a…

解決 SSH Connection closed by foreign host 問題

用 Xshell 連接服務器總是報錯 : Connection closed by foreign host.Disconnected from remote host... 原因可能是 SSH 服務器沒設置保活時間間隔 , 具體設置如下 : 操作 # vim /etc/ssh/sshd_config 添加兩行 , 或去掉注釋 : ClientAliveInterval 60ClientAliveCountMax…

Java基礎——synchronized

synchronized重要&#xff01;重要&#xff01;重要&#xff01;重要的事情說三遍&#xff0c;一定要記下來哦。 Java語言的關鍵字&#xff0c;當它用來修飾一個方法或者一個代碼塊的時候&#xff0c;能夠保證在同一時刻最多只有一個線程執行該段代碼。一、當兩個并發線程訪問同…

C++:MAC安裝Boost庫文件并且使用CLion開發

boost的filestem庫 C在17版本的標準庫中引入了一個filesystem庫&#xff0c;用來處理文件路徑&#xff0c;以及文件訪問。很多編譯器對filesystem庫的支持還不是很好。為了解決這個問題&#xff0c;可以臨時使用boost::filesystem來替代。其實C17標準中的filesystem庫就是從bo…

Java基礎——Java異常處理機制

一、引言 try…catch…finally恐怕是大家再熟悉不過的語句了&#xff0c;而且感覺用起來也是很簡單&#xff0c;邏輯上似乎也是很容易理解。不過&#xff0c;我親自體驗的“教訓”告訴我&#xff0c;這個東西可不是想象中的那么簡單、聽話。不信&#xff1f;那你看看下面的代碼…

clion在使用sqlite3的時候,顯示Undefined symbols for architecture x86_64錯誤的解決辦法

顯示Undefined symbols for architecture x86_64錯誤的原因 1、缺少靜態庫 環境&#xff1a;在模擬器上報錯但在真機上能運行成功&#xff0c;而且報的錯誤來自于第三方庫。原因&#xff1a;architecture x86_64 是指模擬器的架構&#xff0c;意思就是 Crypto 變量在模擬器架…

Java基礎——Java反射機制及IoC原理

一、概念 主要是指程序可以訪問&#xff0c;檢測和修改它本身狀態或行為的一種能力&#xff0c;并能根據自身行為的狀態和結果&#xff0c;調整或修改應用所描述行為的狀態和相關的語義。在java中&#xff0c;只要給定類的名字&#xff0c; 那么就可以通過反射機制來獲得類的所…