設計模式(十) — 命令模式
讓程式暢通執行——命令模式
命令模式的定義
將一個請求封裝成一個物件,從而讓使用者使用不同的請求把客戶端引數化;對請求排隊或者記錄請求日誌,以及支援可撤銷的操作。
命令模式的使用場景
需要抽象出待執行的動作,然後以引數的形式提供出來——類似於過程設計中的回撥機制,而命令模式真是回撥機制的一個面向物件的替代品。 在不同的時刻指定、排列和執行請求。一個命令物件可以有與初始請求無關的生存期。 需要支援取消操作。 支援修改日誌功能,這樣當系統崩潰時,這些修改可以被重做一遍。 需要支援事物操作。
命令模式的例項
角色介紹 Receiver:接收者角色 負責實施或執行一個請求,說得通俗點就是,執行具體邏輯的角色。 Command:命令角色
以俄羅斯方塊為例,俄羅斯方塊有四個按鈕,左右移動、快速落下和變換形狀。四個按鈕相當於請求者,執行具體按鈕命令的邏輯方法可以看作是命令角色。
接收者角色 俄羅斯方塊遊戲
TetrisMachine.class
public class TetrisMachine {
private static final String TAG = "TetrisMachine" ;
/**
* description: 真正處理"向左"操作的邏輯程式碼
*/
public void toLeft() {
Log.i(TAG, "向左");
}
/**
* description: 真正處理"向右"操作的邏輯程式碼
*/
public void toRight() {
Log.i(TAG, "向右");
}
/**
* description: 真正處理"快速下落"操作的邏輯程式碼
*/
public void fastToBottom () {
Log.i(TAG, "快速下落");
}
/**
* description: 真正處理"改變形狀"操作的邏輯程式碼
*/
public void transform() {
Log.i(TAG, "改變形狀");
}
}
命令者抽象
Command.class
public interface Command {
/**
* description: 命令執行方法
*/
void execute();
}
具體命令者 向右
RightCommand.class
public class RightCommand implements Command {
//持有一個接收者俄羅斯方塊遊戲物件的引用
private TetrisMachine tetrisMachine;
public RightCommand(TetrisMachine tetrisMachine) {
this.tetrisMachine = tetrisMachine;
}
@Override
public void execute() {
//呼叫遊戲機裡的具體方法執行操作
tetrisMachine.toRight();
}
}
具體命令者 向左
LeftCommand.class
public class LeftCommand implements Command {
//持有一個接收者俄羅斯方塊遊戲物件的引用
private TetrisMachine tetrisMachine;
public LeftCommand(TetrisMachine tetrisMachine) {
this.tetrisMachine = tetrisMachine;
}
@Override
public void execute() {
//呼叫遊戲機裡的具體方法執行操作
tetrisMachine.toLeft();
}
}
具體命令者 快速落下
FallCommand.class
public class FallCommand implements Command {
//持有一個接收者俄羅斯方塊遊戲物件的引用
private TetrisMachine tetrisMachine;
public FallCommand(TetrisMachine tetrisMachine) {
this.tetrisMachine = tetrisMachine;
}
@Override
public void execute() {
//呼叫遊戲機裡的具體方法執行操作
tetrisMachine.fastToBottom();
}
}
具體命令者 改變形狀
TransformCommand.class
public class TransformCommand implements Command {
//持有一個接收者俄羅斯方塊遊戲物件的引用
private TetrisMachine tetrisMachine;
public TransformCommand(TetrisMachine tetrisMachine) {
this.tetrisMachine = tetrisMachine;
}
@Override
public void execute() {
//呼叫遊戲機裡的具體方法執行操作
tetrisMachine.transform();
}
}
請求者
Buttons.class
public class Buttons {
//向左移動的命令物件引用
private LeftCommand leftCommand;
//向右移動的命令物件引用
private RightCommand rightCommand;
//快速落下的命令物件引用
private FallCommand fallCommand;
//變換形狀的命令物件引用
private TransformCommand transformCommand;
/**
* description: 設定向左移動的命令物件
*/
public void setLeftCommand(LeftCommand leftCommand) {
this.leftCommand = leftCommand;
}
/**
* description: 設定向右移動的命令物件
*/
public void setRightCommand(RightCommand rightCommand) {
this.rightCommand = rightCommand;
}
/**
* description: 設定快速落下的命令物件
*/
public void setFallCommand(FallCommand fallCommand) {
this.fallCommand = fallCommand;
}
/**
* description: 設定變換形狀的命令物件
*/
public void setTransformCommand(TransformCommand transformCommand) {
this.transformCommand = transformCommand;
}
/**
* description: 按下按鈕向左移動
*/
public void toLeft() {
leftCommand.execute();
}
/**
* description: 按下按鈕向右移動
*/
public void toRight() {
rightCommand.execute();
}
/**
* description: 按下按鈕快速下落
*/
public void fall() {
fallCommand.execute();
}
/**
* description: 按下按鈕改變形狀
*/
public void transform() {
transformCommand.execute();
}
}
客戶端
Player.class
public class Player {
public void play() {
//首先要有俄羅斯方塊遊戲
TetrisMachine tetrisMachine = new TetrisMachine();
//根據遊戲我們構成4種命令
LeftCommand leftCommand = new LeftCommand(tetrisMachine);
RightCommand rightCommand = new RightCommand(tetrisMachine);
FallCommand fallCommand = new FallCommand(tetrisMachine);
TransformCommand transformCommand = new TransformCommand(tetrisMachine);
//按鈕可以執行不同的指令
Buttons buttons = new Buttons();
buttons.setLeftCommand(leftCommand);
buttons.setRightCommand(rightCommand);
buttons.setFallCommand(fallCommand);
buttons.setTransformCommand(transformCommand);
//具體按下哪個按鈕玩家說了算
buttons.toLeft();
buttons.toRight();
buttons.fall();
buttons.transform();
}
}
看到這,可能會犯嘀咕,明明可以很簡單的呼叫,像這樣:
TetrisMachine tetrisMachine = new TetrisMachine();
tetrisMachine.toLeft();
tetrisMachine.toRight();
tetrisMachine.fastToBottom();
tetrisMachine.transform();
而且要是增加或者修改一個命令,需要改4處地方,這哪裡是設計模式呀。
這個確實可以兩個類搞定,但是命令模式的主旨是對命令請求者和命令接收者的解耦,方便對命令進行各種控制。什麼叫方便對命令進行控制呢,打個比方,我們需要在按下俄羅斯方塊遊戲的時候記錄命令執行的日誌、命令的執行有某些條件。如果直接用兩個類就會有大量的業務邏輯要在客戶端進行處理,當命令增加,對每個命令的控制增加時,就會在Client裡面產生大量的變化點,這樣耦合就出來了。
小結
優點
更弱的耦合性、更靈活的控制性以及更好的擴充套件性。
缺點
設計模式的通病,類的膨脹,大量衍生類的建立。