(《設計模式解析與實戰——何紅輝,關愛民》讀書筆記)
一、定義
將一個請求封裝成一個對象,從而讓用戶使用不同的請求把客戶端參數化;對請求排隊或者記錄請求日誌,以及支持可撤銷的操作。
比如說一鍵裝機,用戶只需要動一下鼠標,它就會執行下載,裝機等一系列過程。
二、使用場景
(1)需要抽象出待執行的動作,然後以參數的形式提出來——類似於過程設計中的回調機制,而命令模式正是回調機制的一個面向對象的替代品;
(2)在不同的時刻指定、排列和執行請求。一個命令對象可以有與初始請求無關的生存期;
(3)需要支持取消操作;
(4)支持修改日誌功能,這樣當系統崩潰時,這些修改可以被重做一遍;
(5)需要支持事物操作。
三、命令模式的通用模式代碼
/**
* 接收者類:執行具體邏輯的角色
*/
public class Receiver {
/**
* 真正執行具體命令邏輯的方法
*/
public void action() {
System.out.println("執行具體操作");
}
}
/**
* 抽象命令接口:定義所有具體命令類的抽象接口
*/
public interface Command {
/**
* 執行具體操作的命令
*/
void execute();
}
/**
* 具體命令類:執行具體邏輯
*/
public class ConcreteCommand implements Command {
private Receiver mReceiver;// 持有一個對接收者對象的應用
public ConcreteCommand(Receiver receiver) {
super();
this.mReceiver = receiver;
}
@Override
public void execute() {
// 調用接收者的相關方法來執行具體邏輯
mReceiver.action();
}
}
/**
* 請求者類:調用命令對象執行具體的請求
*/
public class Invoker {
private Command mCommand; // 持有一個對相應命令對象的引用
public Invoker(Command command) {
super();
this.mCommand = command;
}
public void action() {
// 調用具體命令對象的相關方法,執行具體命令
mCommand.execute();
}
}
/**
* 客戶類
*/
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();
}
}
運行結果:
四、命令模式的簡單實現
/**
* 接收者角色 俄羅斯方塊遊戲
*/
public class TetrisMachine {
/**
* 真正處理“向左”操作的邏輯代碼
*/
public void toLeft() {
System.out.println("向左");
}
/**
* 真正處理“向右”操作的邏輯代碼
*/
public void toright() {
System.out.println("向右");
}
/**
* 真正處理“快速落下”操作的邏輯代碼
*/
public void fastToBottom() {
System.out.println("快速落下");
}
/**
* 真正處理“改變形狀”操作的邏輯代碼
*/
public void transform() {
System.out.println("改變形狀");
}
}
/**
* 命令者抽象 定義執行方法
*/
public interface Command {
/**
* 命令執行方法
*/
void execute();
}
/**
* 具體命令者 向左移的命令類
*/
public class LeftCommand implements Command {
// 持有一個接收者俄羅斯方塊遊戲對象的引用
private TetrisMachine mMachine;
public LeftCommand(TetrisMachine machine) {
super();
this.mMachine = machine;
}
@Override
public void execute() {
// 調用遊戲機裏的具體方法執行操作
mMachine.toLeft();
}
}
/**
* 具體命令者 向右移的命令類
*/
public class RightCommand implements Command {
// 持有一個接收者俄羅斯方塊遊戲對象的引用
private TetrisMachine mMachine;
public RightCommand(TetrisMachine machine) {
super();
this.mMachine = machine;
}
@Override
public void execute() {
// 調用遊戲機裏的具體方法執行操作
mMachine.toright();
}
}
/**
* 具體命令者 快速落下的命令類
*/
public class FallCommand implements Command {
// 持有一個接收者俄羅斯方塊遊戲對象的引用
private TetrisMachine mMachine;
public FallCommand(TetrisMachine machine) {
super();
this.mMachine = machine;
}
@Override
public void execute() {
// 調用遊戲機裏的具體方法執行操作
mMachine.fastToBottom();
}
}
/**
* 具體命令者 改變形狀的命令類
*/
public class TransformCommand implements Command {
// 持有一個接收者俄羅斯方塊遊戲對象的引用
private TetrisMachine mMachine;
public TransformCommand(TetrisMachine machine) {
super();
this.mMachine = machine;
}
@Override
public void execute() {
// 調用遊戲機裏的具體方法執行操作
mMachine.transform();
}
}
/**
* 請求者類 命令由按鈕發起
*/
public class Buttons {
// 向左移動的命令對象引用
private LeftCommand mLeftCommand;
// 向右移動的命令對象引用
private RightCommand mRightCommand;
// 快速落下的命令對象引用
private FallCommand mFallCommand;
// 變換形狀的命令對象引用
private TransformCommand mTransformCommand;
/**
* 設置向左移動的命令對象
*
* @param leftCommand
*/
public void setLeftCommand(LeftCommand leftCommand) {
this.mLeftCommand = leftCommand;
}
/**
* 設置向右移動的命令對象
*
* @param rightCommand
*/
public void setRightCommand(RightCommand rightCommand) {
this.mRightCommand = rightCommand;
}
/**
* 設置快速落下的命令對象
*
* @param fallCommand
*/
public void setFallCommand(FallCommand fallCommand) {
this.mFallCommand = fallCommand;
}
/**
* 設置變換形狀的命令對象
*
* @param transformCommand
*/
public void setTransformCommand(TransformCommand transformCommand) {
this.mTransformCommand = transformCommand;
}
/**
* 按下按鈕向左移動
*/
public void toLeft() {
mLeftCommand.execute();
}
/**
* 按下按鈕向右移動
*/
public void toright() {
mRightCommand.execute();
}
/**
* 按下按鈕快速落下
*/
public void fastToBottom() {
mFallCommand.execute();
}
/**
* 按下按鈕變換形狀
*/
public void transform() {
mTransformCommand.execute();
}
}
/**
* 客戶類
*/
public class Player {
public static void main(String[] args) {
// 構造一個俄羅斯方塊遊戲
TetrisMachine machine = new TetrisMachine();
// 根據遊戲構造四種命令
LeftCommand leftCommand = new LeftCommand(machine);
RightCommand rightCommand = new RightCommand(machine);
FallCommand fallCommand = new FallCommand(machine);
TransformCommand transformCommand = new TransformCommand(machine);
// 按鈕可以執行不同的命令
Buttons buttons = new Buttons();
buttons.setLeftCommand(leftCommand);
buttons.setRightCommand(rightCommand);
buttons.setFallCommand(fallCommand);
buttons.setTransformCommand(transformCommand);
// 具體按下哪個按鈕玩家說了算
buttons.toLeft();
buttons.toright();
buttons.fastToBottom();
buttons.transform();
}
}
運行結果:
大部分開發者或許使用一下代碼:
// 構造一個俄羅斯方塊遊戲
TetrisMachine machine = new TetrisMachine();
machine.toLeft();
machine.toright();
machine.fastToBottom();
machine.transform();
同樣可以得到期望的結果,而且代碼也少,邏輯簡單,但是對於開發者來說是方便,如果有一天開發者不再負責這個項目,這樣的邏輯留給後來者,就不會有人覺得方便了,而且設計模式有一條原則:對修改關閉,對擴展開放。
而且命令模式另一個好處是可以實現命令記錄的功能,並可以在需要時恢復。
五、總結
優點:
(1)弱耦合性;
(2)靈活的控制性;
(3)更好的擴展形。
缺點:
類的膨脹,大量衍生類的創建。