《Android源碼設計模式解析與實戰》讀書筆記(十一)


第十一章、命令模式

命令模式是行為型模式之一。總體來說並不難理解,只是比較繁瑣,他會將簡單的調用關系解耦成多個部分,增加類的復雜度,但是即便如此,命令模式的結構依然清晰。

1.定義

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

2.使用場景

(1)需要抽出待執行的動作,然后以參數的形式提供出來。

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

(3)需要支持操作取消。

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

(5)需要支持事務操作。

3.簡單實現

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

接收者角色:推箱子游戲

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;
}

@Override
public void execute() {
//調用具體命令
pushBox.toLeft();
}

@Override
public void getCommand() {
System.out.print("向左-->");
}
}

具體命令者,右移命令類

public class RightCommand implements Command{

//持有一個接受推箱子游戲對象的引用
private PushBox pushBox;

public RightCommand(PushBox pushBox){
this.pushBox = pushBox;
}

@Override
public void execute() {
//調用具體命令
pushBox.toRight();
}

@Override
public void getCommand() {
System.out.print("向右-->");
}
}

具體命令者,上移命令類

public class UpCommand implements Command{

//持有一個接受推箱子游戲對象的引用
private PushBox pushBox;

public UpCommand(PushBox pushBox){
this.pushBox = pushBox;
}

@Override
public void execute() {
//調用具體命令
pushBox.toUp();
}

@Override
public void getCommand() {
System.out.print("向上-->");
}
}

具體命令者,下移命令類

public class DownCommand implements Command{

//持有一個接受推箱子游戲對象的引用
private PushBox pushBox;

public DownCommand(PushBox pushBox){
this.pushBox = pushBox;
}

@Override
public void execute() {
//調用具體命令
pushBox.toDown();
}

@Override
public void getCommand() {
System.out.print("向下-->");
}
}

具體命令者,撤銷命令類

public class RevokeCommand implements Command{

//持有一個接受推箱子游戲對象的引用
private PushBox pushBox;

public RevokeCommand(PushBox pushBox){
this.pushBox = pushBox;
}

@Override
public void execute() {
//調用具體命令
pushBox.revoke();;
}
@Override
public 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類無需知道具體的內部實現。

設計模式的使用之前也有提到,主要是要看當前場景的復雜度和以后的需求進行擴展、維護等方面,完全使用設計模式也是不提倡的,這就需要設計者權衡利弊了。

4.Android源碼中的命令模式實現

1.PackageHandler

PackageManagerService中,其對包的相關消息處理右其內部類PackageHandler承擔,其將需要處理的請求作為對象通過消息傳遞給相關的方法,而對於包的安裝、移動以及包大小的測量則分別封裝為HandlerParams的具體子類InstallParamsMoveParamsMeasureParams。源碼如下:

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也是一個抽象命令者。

5.總結

1.優點

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

2.缺點

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

6.參考

鏈接:命令模式


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com