設計模式之命令模式(Command Pattern)

一.什麼是命令模式?java

命令模式,封裝了方法調用細節,以解耦請求者與執行者,具體流程以下:數據庫

1.從請求者(客戶)的角度看編輯器

請求者(客戶)發出請求 -> 調用者(系統)構造命令對象封裝請求 -> 調用者調用命令對象的指定方法(請求被執行)ide

很明顯,請求者根本不知道執行者是誰,更不知道具體執行細節。固然請求者自己也並不關心這些,它只要知道請求被執行了就好。測試

2.從執行者(低層組件)的角度看spa

執行者(低層組件)被調用 -> 執行者調用內部方法(請求被執行)線程

一樣的,執行者根本不知道請求者是誰,甚至不清楚調用者,不過不要緊,執行者只要本本分分的作好本職工做就行了,不必知道領導的狀況。日誌

3.從調用者(系統)的角度看對象

接到請求 -> 建立命令對象封裝請求 -> 在適當的時候調用命令對象的動做來執行請求(請求被執行)blog

調用者不知道執行者是誰,也不清楚請求者,它只負責構造命令並控制命令被執行,這就足夠了。


從上面能夠看出各個對象之間的低耦合關係:

請求者(客戶)與執行者(低層組件)被完全解耦,做爲中間人的調用者也不瞭解請求者與執行者的具體細節,它們被很好的保護了起來

這正是咱們想要的。

二.舉個例子

現實世界中任何一個稍微複雜的子系統都應當有一套命令,好比餐館的運行機制:

顧客A來到餐館點一碗麪(發出請求) -> 櫃檯服務員記錄下來(建立命令) -> 服務員把小票扔給廚房 -> 廚師C很快作好了一碗麪(請求被執行)

顧客不知道將由誰來作這碗麪,櫃檯服務員也不知道,廚師不知道是誰點了這碗麪,只知道作完面就能夠休息了

是否是與命令模式很相像?


不妨用代碼來實現上面的機制

首先,咱們須要一個命令接口,畢竟命令纔是命令模式的核心,沒有命令,一切都是空想

package CommandPattern;

/**
 * @author ayqy
 * 定義Command接口
 */
public interface Command {
	public abstract void execute();//只須要定義一個統一的執行方法
}

有了命令還須要執行者,不然只有將軍沒有小兵,餐館的執行者固然是廚師:

package CommandPattern;

/**
 * @author ayqy
 * 定義Chef基類
 */
public abstract class Chef {
	//在此定義廚師的公共屬性
	
	/**
	 * 定義烹飪方法
	 */
	public abstract void cook();
	//在此定義其它有用的方法
}

咱們還須要實現具體的廚師,術業有專攻:

作面的廚師:

package CommandPattern;

/**
 * @author ayqy
 * 定義專業作面的廚師
 */
public class NoodlesChef extends Chef{

	@Override
	public void cook() {
		System.out.println("作好了一碗美味的拉麪");
	}
}

作餅的廚師:

package CommandPattern;

/**
 * @author ayqy
 * 定義專業作餅的廚師
 */
public class PieChef extends Chef{

	@Override
	public void cook() {
		System.out.println("作好了一塊香噴噴的大餅");
	}
}

有了小兵,有了將軍,咱們還須要一套完整的命令:

package CommandPattern;

/**
 * @author ayqy
 * 實現具體NoodlesCommand
 */
public class NoodlesCommand implements Command{
	private NoodlesChef chef;//專業作面的廚師
	
	public NoodlesCommand(){
		chef = new NoodlesChef();
	}

	@Override
	public void execute() {
		chef.cook();
		//調用其它須要的方法
	}
}
package CommandPattern;

/**
 * @author ayqy
 * 實現具體PieCommand
 */
public class PieCommand implements Command{
	private PieChef chef;//專業作餅的廚師
	
	public PieCommand(){
		chef = new PieChef();
	}

	@Override
	public void execute() {
		chef.cook();
		//調用其它須要的方法
	}
}

備工做作好了,餐館能夠開張了

三.效果示例

須要一個Test類:

package CommandPattern;

/**
 * @author ayqy
 * 實現測試類
 */
public class Test {
	
	public static void main(String[] args) {
		System.out.println("Command Pattern餐館開張。。");
		System.out.println("第一位客戶X先生");
		System.out.println("X先生:你好,我須要一碗麪,我餓極了");
		NoodlesCommand nCmd = new NoodlesCommand();
		System.out.println("櫃檯服務員:好的,我已經記下了,立刻就好");
		System.out.println("櫃檯服務員:廚房~~,接單");
		nCmd.execute();
		System.out.println("X先生:真快啊!");
		
		System.out.println();
		
		System.out.println("第二位客戶XX先生");
		System.out.println("XX先生:你好,我須要一塊餅,20分鐘後來取");
		PieCommand pCmd = new PieCommand();
		System.out.println("櫃檯服務員:好的,我已經記下了");
		System.out.println("15分鐘後");
		System.out.println("櫃檯服務員:廚房~~,接單");
		pCmd.execute();
		System.out.println("XX先生:真準時啊!");
	}
}

結果示例:


從例子能夠看出:

1.調用者(櫃檯服務員)能夠控制具體執行時機,但對具體執行者(廚師)的細節徹底不清楚

2.請求者(顧客)徹底不知道餐館的運行機制,不知道點的餐是廚師作的仍是服務員作的或者是從隔壁買的。。

3.執行者(廚師)徹底不知道請求者的狀況,它只作了本職工做,其它的什麼都不知道

四.命令模式的擴展

1.宏命令(多條命令順序執行)

咱們能夠定義「命令的命令」來實現(這種特殊的命令的execute方法內部是順序調用其它若干命令的execute方法。。)

2.撤銷

假如來了不少顧客,點了不少份餐點,過了一下子有幾個顧客等不及了須要撤銷,咱們如何實現?

維護一個命令列表,記錄已經建立的命令,撤銷時須要找到對應的命令,執行撤銷操做

固然,前提是命令對象支持撤銷,咱們須要作一些修改:

package CommandPattern;

/**
 * @author ayqy
 * 定義Command接口
 */
public interface Command {
	public abstract void execute();//只須要定義一個統一的執行方法
	
	public abstract void undo();//定義統一的撤銷方法
}

各個命令的撤銷操做可能不一樣,所以定義爲抽象方法,由子類來實現具體操做

*如何支持多步順序撤銷?

餐館的例子可能不須要這樣的功能,不妨想一想另外一個情景,文本編輯器

用戶發出了一系列命令,完成了一些列操做,後來發現並不須要這樣作,用戶會撤銷修改(Ctrl + Z),這時咱們須要執行相反的操做對內容做以還原,要如何實現?

仍是要先實現各個命令的undo行爲(執行與execute相反順序的操做便可),除此以外,咱們還須要一個棧來記錄已經被執行過的操做,以支持撤銷到初始狀態

3.隊列請求

咱們能夠創建一個工做線程,負責全部運算,想象有一個通道,輸入是一條條不一樣命令,輸出是命令的執行結果

可能上一刻工做線程在作大餅,下一刻已經出去買菜了。。

*這樣作有什麼好處?

能夠把運算限制在指定的幾個線程中,加以控制

4.日誌請求

多用於數據庫管理系統的實現,咱們須要把一系列的操做記錄下來(如寫在硬盤上),在遇到系統故障時讀出來以恢復數據,如何實現?

利用對象的序列化把對象保存起來(記錄日誌),在須要的時候反序列化(恢復事務)

五.總結

命令模式能夠有效地解耦請求者與執行者,還能夠提供一些額外的好處(好比支持撤銷操做、隊列請求、記錄日誌等等)

相關文章
相關標籤/搜索