設計模式系列之「門面模式」

YoungManSter發表於2018-02-12

《三國演義》中有曰:劉備、諸葛亮趁曹操赤壁之戰失利,大肆擴充地盤,先後佔領荊州大部地區,引起東吳孫權的警惕。為了限制劉備勢力的發展,魯肅奉命向劉備討還荊州,但遭到拒絕。東吳大都督周瑜向孫權獻計:趁劉備的甘夫人病故,用孫權的妹妹孫仁為誘餌,將劉備“賺到南徐,妻子不能勾得,幽囚在獄中”。 但是,這個詭計被諸葛亮一眼識破。他將計就計,讓劉備“擇日便去就親”,並派趙雲前去保護,並給了趙雲三個錦囊,教趙雲“依次而行”。結果,使東吳“賠了夫人又折兵”。此為諸葛亮的錦囊三妙計。

一、三妙計

妙計一:見喬國老,並把劉備娶親的事情搞得東吳人盡皆知。

妙計二:用謊言(曹操打荊州)騙泡在溫柔鄉里的劉備回去。

妙計三:讓孫夫人擺平東吳的追兵,她是孫權妹妹,東吳將領懼她三分。

二、妙計的兩種實施方案

1.把三個錦囊直接交給劉備,讓劉備根據情況開啟錦囊。

2.把錦囊交給趙雲,趙雲按照諸葛亮的囑咐,依次按照情況使用錦囊。

三、劉備用妙計

①妙計的介面

public interface Strategy {
	//妙計內容
	public void carryOut();
}
複製程式碼

②妙計一

public class StrategyOne implements Strategy {

	@Override
	public void carryOut() {
		System.out.println("見喬國老,並把劉備娶親的事情搞得東吳人盡皆知。");
	}
}
複製程式碼

③妙計二

public class StrategyTwo implements Strategy {

	@Override
	public void carryOut() {
		System.out.println("用謊言(曹操打荊州)騙泡在溫柔鄉里的劉備回去。");
	}
}
複製程式碼

④妙計三

public class StrategyThree implements Strategy {

	@Override
	public void carryOut() {
		System.out.println("讓孫夫人擺平東吳的追兵。");
	}
}
複製程式碼

⑤劉備使用妙計

public class Client {
	public static void main(String[] args) {
		//劉備一行人到達南徐的時候,開啟第一個錦囊
		Strategy strategyOne=new StrategyOne();
		strategyOne.carryOut();
		//周瑜和孫權通過計謀使劉備沉迷在溫柔鄉無法自拔
		Strategy strategyTwo=new StrategyTwo();
		strategyTwo.carryOut();
		//周瑜看計謀不行,出兵攔殺劉備
		Strategy strategyThree=new StrategyThree();
		strategyThree.carryOut();
	}
}
複製程式碼

輸出的結果為:

//劉備一行人到達南徐的時候,開啟第一個錦囊
見喬國老,並把劉備娶親的事情搞得東吳人盡皆知。

//周瑜和孫權通過計謀使劉備沉迷在溫柔鄉無法自拔
用謊言(曹操打荊州)騙泡在溫柔鄉里的劉備回去。

//周瑜看計謀不行,出兵攔殺劉備
讓孫夫人擺平東吳的追兵。
複製程式碼

忽略其他正常因素(諸葛亮不可能把錦囊交給劉備實施),程式碼角度分析這種方案帶來的問題

  • 錦囊使用是有前提的,要根據每個階段來使用對應的錦囊,在編寫程式碼中要清楚它們的順序,一旦出錯就會造成劉備死翹翹。這在物件導向的程式設計中是極度地不適合,它根本就沒有完成一個類所具有的單一職責。

  • 外界訪問直接深入到子系統內部,相互之間是一種強耦合關係,這樣的強依賴是系統設計所不能接受的。

  • 子系統的內部方法直接暴露給外部呼叫,安全性低。

  • 當錦囊數越來越多的時候,那麼Client就需要呼叫更多錦囊類來實現,這就會增加Client的實現難度,維護更加困難。

四、趙雲協助用妙計(門面模式)

諸葛亮能夠想到應對之策,當然也會考慮到讓誰和如何實施這三個錦囊才能夠讓劉備順利化險為夷。綜合上面劉備親自實施錦囊妙計帶來的問題,安排趙雲保管錦囊並在適當的時機協助實施才是上上之策。

1.UML實現

設計模式系列之「門面模式」

增加了一個ZhaoYunFacadee類,負責對錦囊實施過程進行封裝,然後高層模組只要和它有互動就成。

2.增加的程式碼模組

①趙雲充當門面

public class ZhaoYunFacade {
	//妙計一
	private Strategy strategyOne=new StrategyOne();
	//妙計二
	private Strategy strategyTwo=new StrategyTwo();
	//妙計三
	private Strategy strategyThree=new StrategyThree();

	//三妙計統一讓趙雲協助實施
	public void carryOut(){
		//劉備一行人到達南徐的時候,開啟第一個錦囊
		strategyOne.carryOut();
		//周瑜和孫權通過計謀使劉備沉迷在溫柔鄉無法自拔
		strategyTwo.carryOut();
		//周瑜看計謀不行,出兵攔殺劉備
		strategyThree.carryOut();
	}
}
複製程式碼

劉備在遇到困難的時候,只要讓趙雲根據諸葛亮的錦囊進行處理,這多簡單,Client減少了很多工作。

②Client

public class Client {
	public static void main(String[] args) {
		//劉備遇到困難,只要通過趙雲按照錦囊實施,就可化險為夷
		ZhaoYunFacade zhaoYunFacade=new ZhaoYunFacade();
	zhaoYunFacade.carryOut();;
	}	
}
複製程式碼

輸出的結果為:

//劉備一行人到達南徐的時候,開啟第一個錦囊
見喬國老,並把劉備娶親的事情搞得東吳人盡皆知。

//周瑜和孫權通過計謀使劉備沉迷在溫柔鄉無法自拔
用謊言(曹操打荊州)騙泡在溫柔鄉里的劉備回去。

//周瑜看計謀不行,出兵攔殺劉備
讓孫夫人擺平東吳的追兵。
複製程式碼

執行結果是相同的。場景類簡化了很多,只要與ZhaoYunFacade互動就成了,其他的什麼都不用管,什麼時候使用錦囊、怎麼用都不用管,只要呼叫ZhaoYunFacade提供的方法,就可以得到想要的妙計,這種方式不僅簡單,而且擴充套件性還非常好,同時不改變子系統對外暴露的介面、方法,只改變內部的處理邏輯,其他兄弟模組的呼叫產生了不同的結果。

3.兩種方案的程式結構圖

  • 沒有采用門面模式

設計模式系列之「門面模式」

  • 採用門面模式

設計模式系列之「門面模式」

總的來說,門面物件是外界訪問子系統內部的唯一通道,不管子系統內部是多麼雜亂無章。

五、門面模式的介紹

1.門面模式的定義

門面模式(Facade Pattern)也叫做外觀模式。要求一個子系統的外部與其內部的通訊必須通過一個統一的物件進行。門面模式提供一個高層次的介面,使得子系統更易於使用。

2.中介模式的角色介紹

設計模式系列之「門面模式」

  • Facade門面角色
    客戶端可以呼叫這個角色的方法。此角色知曉子系統的所有功能和責任。一般情況下,本角色會將所有從客戶端發來的請求委派到相應的子系統去,也就說該角色沒有實際的業務邏輯,只是一個委託類。

  • subsystem子系統角色
    可以同時有一個或者多個子系統。每一個子系統都不是一個單獨的類,而是一個類的集合。子系統並不知道門面的存在。對於子系統而言,門面僅僅是另外一個客戶端而已。

3.門面模式的使用場景

  • 為一個複雜的模組或子系統提供一個供外界訪問的介面。

  • 為降低個人程式碼質量對整體專案的影響風險,只能在指定的子系統中開發,然後再提供門面介面進行訪問操作。

  • 子系統相對獨立——外界對子系統的訪問只要黑箱操作即可。

六、門面模式優缺點

1.優點

減少系統的相互依賴。上面也提到不使用門面模式,外界訪問直接深入到子系統內部,相互之間是一種強耦合關係這樣的強依賴是系統設計所不能接受的,門面模式的出現就很好地解決了該問題,所有的依賴都是對門面物件的依賴,與子系統無關。

提高了靈活性。依賴減少了,靈活性自然提高了。不管子系統內部如何變化,只要不影響到門面物件,就沒有什麼問題了。

提高安全性。訪問子系統的哪些業務就開通哪些邏輯,不需要把子系統的內部方法直接暴露給外部呼叫。

2.缺點

門面模式最大的缺點就是不符合開閉原則,對修改關閉,對擴充套件開放。系統投產後發現問題唯一能做的一件事就是修改門面角色的程式碼,這個風險相當大。

七、簡單對比門面模式和中介模式的區別

1.從定義上,門面模式為複雜的子系統提供一個統一的訪問介面,它定義的是一個高層介面,該介面使得子系統更加容易使用,避免外部模組深入到子系統內部而產生與子系統內部細節耦合的問題。中介者模式使用一箇中介物件來封裝一系列同事物件的互動行為,它使各物件之間不再顯式地引用,從而使其耦合鬆散,建立一個可擴充套件的應用架構。

2.從功能上,門面模式只是增加了一個門面,它對子系統來說沒有增加任何的功能,子系統若脫離門面模式是完全可以獨立執行的。而中介者模式則增加了業務功能,它把各個同事類中的原有耦合關係移植到了中介者,同事類不可能脫離中介者而獨立存在。

3.從關係上,對門面模式來說,子系統不知道有門面存在,而對中介者來說,每個同事類都知道中介者存在,因為要依靠中介者調和同事之間的關係,它們對中介者非常瞭解。

4.從封裝程度上,門面模式是一種簡單的封裝,所有的請求處理都委託給子系統完成,而中介者模式則需要有一箇中心,由中心協調同事類完成,並且中心本身也完成部分業務,它屬於更進一步的業務功能封裝。

八、總結

門面模式是一個很好的封裝方法,一個子系統比較複雜時,比如演算法或者業務比較複雜,就可以封裝出一個或多個門面出來,專案的結構簡單,而且擴充套件性非常好。還有,對於一個較大專案,為了避免人員帶來的風險,也可以使用門面模式。

Android技術交流吧

相關文章