Android編程設計模式之命令模式詳解
本文實例講述了Android編程設計模式之命令模式。分享給大家供大家參考,具體如下:
一、介紹
命令模式(Command Pattern),是行為型設計模式之一。命令模式相對于其他的設計模式來說并沒有那么多的條條框框,其實它不是一個很”規(guī)范“的模式,不過,就是基于這一點,命令模式相對于其他的設計模式更為靈活多變。我們接觸比較多的命令模式個例無非就是程序菜單命令,如在操作系統(tǒng)中,我們點擊”關機“命令,系統(tǒng)就會執(zhí)行一系列的操作,如先是暫停處理事件,保存系統(tǒng)的一些配置,然后結束程序進程,最后調(diào)用內(nèi)核命令關閉計算機等,對于這一系列的命令,用戶不用去管,用戶只需點擊系統(tǒng)的關機按鈕即可完成如上一系列的命令。而我們的命令模式其實也與之相同,將一系列的方法調(diào)用封裝,用戶只需調(diào)用一個方法執(zhí)行,那么所有這些被封裝的方法就會被挨個執(zhí)行調(diào)用。
二、定義
將一個請求封裝成一個對象,從而讓用戶使用不同的請求把客戶端參數(shù)化;對請求排隊或者記錄請求日志,以及支持可撤銷的操作。
三、使用場景
需要抽象出待執(zhí)行的動作,然后以參數(shù)的形式提供出來——類似于過程設計中的回調(diào)機制,而命令模式正是回調(diào)機制的一個面向?qū)ο蟮奶娲贰?/p>
在不同的時刻指定、排列和執(zhí)行請求。一個命令對象可以有與初始請求無關的生存期。
需要支持取消操作。
支持修改日志功能,這樣當系統(tǒng)崩潰時,這些修改可以被重做一遍。
需要支持事務操作。
四、命令模式的UML類圖
UML類圖:

通用模式代碼:
接收者類:
public class Receiver {
/*
* 真正執(zhí)行具體命令邏輯的方法
*/
public void action(){
System.out.println("具體執(zhí)行");
}
}
抽象命令接口:
public interface Command {
/*
* 執(zhí)行具體操作的命令
*/
void excute();
}
具體命令類:
public class ConcreteCommand implements Command {
private Receiver receiver;
public ConcreteCommand(Receiver receiver) {
this.receiver = receiver;
}
@Override
public void excute() {
//調(diào)用接收者的相關方法來執(zhí)行具體邏輯
receiver.action();
}
}
請求者類:
public class Invoker {
private Command command;
public Invoker(Command command) {
this.command = command;
}
public void action(){
//調(diào)用具體命令對象的相關方法,執(zhí)行具體命令
command.excute();
}
}
客戶類:
public class Client {
public static void main(String[] args) {
//構造一個接收者對象
Receiver receiver = new Receiver();
//根據(jù)接收者對象構造一個命令對象
Command command = new ConcreteCommand(receiver);
//根據(jù)具體的對象構造請求者對象
Invoker invoker = new Invoker(command);
//執(zhí)行請求方法
invoker.action();
}
}
角色介紹:
Receiver:接收者角色,該類負責具體實施或執(zhí)行一個請求,說得通俗點就是,執(zhí)行具體邏輯的角色,以本節(jié)開頭的”關機“命令操作為例,其接收者角色就是真正執(zhí)行各項關機邏輯的底層代碼。任何一個類都可以成為一個接收者,而在接收者類中封裝具體操作邏輯的方法我們則稱為行動方法。
Command:命令角色,定義所有具體命令類的抽象接口。
ConcreteCommand:具體命令角色,該類實現(xiàn)了Command接口,在execute方法中調(diào)用接收者角色的相關方法,在接收者和命令執(zhí)行的具體行為之間加以弱耦合。而execute則通常稱為執(zhí)行方法,如本文開頭所述”關機“的操作實現(xiàn),具體可能還包含很多相關的操作,比如保存數(shù)據(jù)、關閉文件、結束進程等,如果將這一系列具體的邏輯處理看作接收者,那么調(diào)用這些具體邏輯的方法就可以看作是執(zhí)行方法。
Invoker:請求者角色,該類的職責就是調(diào)用命令對象執(zhí)行具體的請求,相關的方法我們稱為行動方法,還是用”關機“為例,”關機“這個菜單命令一般就對應一個關機方法,我們點擊了”關機“命令后,由這個關機方法去調(diào)用具體的命令執(zhí)行具體的邏輯,這里的”關機“對應的這個方法就可以看作是請求者。
Client:客戶端類,Client可以創(chuàng)建具體的命令對象,并且設置命令對象的接收者。Tips:不能把Clinet理解為我們平常說的客戶端,這里的Client是一個組裝命令對象和接受者對象的角色,或者你把它理解為一個裝配者。
五、簡單實現(xiàn)
以推箱子游戲為例,一般游戲中會有五個按鈕,分別是左移、右移、下移、上移和撤銷。那么玩游戲的人就是客戶端,五個按鈕就是調(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í)行撤銷命令
*/
public void revoke(){
System.out.println("撤銷");
}
}
抽象命令接口:
public interface Command {
/**
* 命令執(zhí)行方法
*/
void execute();
/**
* 獲取命令類型
*/
void getCommand();
}
具體命令者,左移命令類:
public class LeftCommand implements Command{
//持有一個接受推箱子游戲?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("向左-->");
}
}
具體命令者,右移命令類:
public class RightCommand implements Command{
//持有一個接受推箱子游戲?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("向右-->");
}
}
具體命令者,上移命令類:
public class UpCommand implements Command{
//持有一個接受推箱子游戲?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("向上-->");
}
}
具體命令者,下移命令類:
public class DownCommand implements Command{
//持有一個接受推箱子游戲?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("向下-->");
}
}
具體命令者,撤銷命令類:
public class RevokeCommand implements Command{
//持有一個接受推箱子游戲?qū)ο蟮囊?
private PushBox pushBox;
public RevokeCommand(PushBox pushBox){
this.pushBox = pushBox;
}
@Override
public void execute() {
//調(diào)用具體命令
pushBox.revoke();;
}
@Override
public void getCommand() {
}
}
請求者類,命令由按鈕發(fā)起:
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>();//用于記錄命令動作
/**
* 獲取執(zhí)行命令
*/
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);
}
}
客戶端調(diào)用:
public class Client {
public static void main(String[] args) {
//首先創(chuàng)建游戲
PushBox pushBox = new PushBox();
//根據(jù)游戲構造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í)行結果:
向左 向下 向下 向右 向左-->向下-->向下-->向右--> 撤銷 向上 向左 向下 向上 向左-->向下-->向下-->向上-->向左-->向下-->向上-->
在這么長的代碼之后是不是覺得很煩瑣,明明可以很簡單的實現(xiàn),如下:
public class Client {
public static void main(String[] args) {
//首先創(chuàng)建游戲
PushBox pushBox = new PushBox();
pushBox.toDown();
pushBox.toRight();
pushBox.toUp();
}
}
其實設計模式有一個重要的原則:對修改關閉對擴展開放。如果使用如上的簡單方式,那么以后的修改只能去修改PushBox類,然后修改Client類,這顯然違反了這一原則。如果使用命令模式,那么Client類無需修改,只需要修改PushBox類的內(nèi)部操作,Client類無需知道具體的內(nèi)部實現(xiàn)。
六、Android源碼中的命令模式
1、PackageHandler
PackageManagerService中,其對包的相關消息處理右其內(nèi)部類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也是一個抽象命令者。
七、總結
優(yōu)點:
命令模式的封裝性很好,更弱的耦合性,更靈活的控制性以及更好的擴展性。
缺點:
類的膨脹,大量衍生類的創(chuàng)建。
更多關于Android相關內(nèi)容感興趣的讀者可查看本站專題:《Android開發(fā)入門與進階教程》、《Android調(diào)試技巧與常見問題解決方法匯總》、《Android基本組件用法總結》、《Android視圖View技巧總結》、《Android布局layout技巧總結》及《Android控件用法總結》
希望本文所述對大家Android程序設計有所幫助。
相關文章
flutter RotationTransition實現(xiàn)旋轉(zhuǎn)動畫
這篇文章主要為大家詳細介紹了flutter RotationTransition實現(xiàn)旋轉(zhuǎn)動畫,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-07-07
Android用過TextView實現(xiàn)跑馬燈效果的示例
本篇文章主要介紹了Android用過TextView實現(xiàn)跑馬燈效果的示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08
詳解Android 語音播報實現(xiàn)方案(無SDK)
本篇文章主要介紹了詳解Android 語音播報實現(xiàn)方案(無SDK),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-11-11
Android實現(xiàn)捕獲未知異常并提交給服務器的方法
這篇文章主要介紹了Android實現(xiàn)捕獲未知異常并提交給服務器的方法,涉及Android的異常與錯誤處理機制相關操作技巧,需要的朋友可以參考下2016-08-08
Android 網(wǎng)絡圖片查看器與網(wǎng)頁源碼查看器
本篇文章主要介紹了Android 網(wǎng)絡圖片查看器與網(wǎng)頁源碼查看器的相關知識。具有很好的參考價值。下面跟著小編一起來看下吧2017-04-04
詳解Android中Intent傳遞對象給Activity的方法
這篇文章主要介紹了Android中Intent傳遞對象給Activity的方法,文章中對Activity的生命周期等知識先作了簡要的介紹,需要的朋友可以參考下2016-04-04

