外觀模式是一種使用頻率非常高,但理解較為簡單的設計模式,通過引入一個外觀角色來簡化客戶端與子系統之間的操作,為復雜的子系統呼叫提供一個統一的入口,使子系統與客戶端的耦合度降低,且客戶端呼叫非常方便,
模式動機
在大多數情況下,一個網站都會提供一個網站首頁,網站首頁一般作為整個網站的入口,提供了通往各個子欄目的超鏈接,用戶通過該首頁即可進入子欄目獲取所需資訊,對于用戶而言只需記住網站首頁網站 URL,而無須記住每個子欄目的網址,網站首頁作為用戶訪問子欄目的入口,是網站的外觀角色,用戶通過它可以方便地訪問子欄目,當然也可以繞過它直接訪問子欄目,

如果沒有外觀角色,即沒有為網站提供一個首頁,每個用戶需要記住所有子欄目的 URL,不僅會讓系統耦合度增加,用戶與系統的互動也會例外復雜,
模式定義
外部與一個子系統的通信必須通過一個統一的外觀物件進行,為子系統中的一組介面提供一個一致的界面,外觀模式定義了一個高層介面,這個介面使得這一子系統更加易于使用,
模式結構
外觀模式沒有一個一般化的類圖描述,這里以類圖作為外觀模式的描述形式之一,

-
Facade(外觀角色)
在客戶端可以呼叫這個角色的方法,在外觀角色中可以知道相關子系統的功能和責任;在正常情況下,它將所有從客戶端發來的請求委派到相應子系統,傳遞給相應的子系統物件處理,
-
SubSystem(子系統角色)
在軟體系統中可以同時有一個或多個子系統角色,每一個子系統都可以被客戶端直接呼叫,或被外觀角色呼叫,處理外觀類傳過來的請求;子系統并不知道外觀的存在,對于子系統而言,外觀僅僅是另外一個客戶端而言,
實體之電源總開關
一個電源總開關可以控制四盞燈、一個風扇、一臺空調和一臺電視機的啟動和關閉,通過該電源總開關可以同時控制所有上述電器設備,使用外觀模式設計該系統,

-
子系統類 Light
public class Light { private String position; public Light(String position) { this.position = position; } public void on() { System.out.println(this.position + "燈打開"); } public void off() { System.out.println(this.position + "燈打開"); } } -
子系統類 Fan
public class Fan { public void on() { System.out.println("風扇打開"); } public void off() { System.out.println("風扇關閉"); } } -
子系統類 AirConditioner
public class AirConditioner { public void on() { System.out.println("空調打開"); } public void off() { System.out.println("空調關閉"); } } -
子系統類 Televison
public class Television { public void on() { System.out.println("電視機打開"); } public void off() { System.out.println("電視機關閉"); } } -
外觀類 GeneralSwitchFacade
public class GeneralSwitchFacade { private Light[] lights = new Light[4]; private Fan fan; private AirConditioner ac; private Television tv; public GeneralSwitchFacade() { lights[0] = new Light("左前"); lights[1] = new Light("右前"); lights[2] = new Light("左后"); lights[3] = new Light("右后"); fan = new Fan(); ac = new AirConditioner(); tv = new Television(); } public void on() { lights[0].on(); lights[1].on(); lights[2].on(); lights[3].on(); fan.on(); ac.on(); tv.on(); } public void off() { lights[0].off(); lights[1].off(); lights[2].off(); lights[3].off(); fan.off(); ac.off(); tv.off(); } } -
測驗類 Client
public class Client { public static void main(String[] args) { GeneralSwitchFacade gsf = new GeneralSwitchFacade(); gsf.on(); System.out.println("-------------"); gsf.off(); } } -
運行結果

模式優缺點
外觀模式優點如下:
- 對客戶屏蔽子系統組件,使得子系統使用起來更加方便
- 實作了子系統和客戶之間的松耦合關系,子系統組件變化不會影響到客戶類
- 簡化了系統在不同平臺之間的移植程序,因為一個子系統的修改對其他子系統沒有任何影響
- 只是提供了一個訪問子系統的統一入口,并不影響用戶直接使用子系統類
外觀模式缺點如下:
- 不能很好限制客戶使用子系統類
- 在不引入抽象外觀類的情況下,增加新的子系統可能需修改外觀類和客戶端的源代碼,違背了開閉原則
模式適用環境
以下情況可以使用外觀模式:
- 當要為一個復雜子系統提供一個簡單介面時可以使用外觀模式
- 客戶程式與多個子系統之間存在很大依賴性
- 在層次化結構中,可以使用外觀模式定義系統每一層的入口,層與層之間不直接聯系,通過外觀類建立聯系,降低層之間的耦合度
一個系統有多個外觀類
為了節約資源,一般將外觀類設計為單例類,但這并不意味整個系統只有一個外觀類,一個系統中可以設計多個外觀類,每個外觀類都負責和一些特定的子系統互動,向用戶提供相應的業務功能
不要試圖通過外觀類為子系統增加新行為
外觀模式的用意是為子系統提供一個集中化和簡化的溝通渠道,而不是向子系統加入新的行為,新的行為的增加應通過修改原有子系統類或增加新的子系統類來實作,不能通過外觀類來實作
外觀模式和迪米特法則
迪米特法則要求你與直接的朋友通信,外觀模式創造出一個外觀物件,將客戶端所涉及的屬于一個子系統的協作伙伴的數量減到最少,外觀類充當了客戶類與子系統類之間的第三者,降低了客戶類與子系統之間的耦合度,外觀模式是實作代碼重構以達到迪米特法則要求的一個強有力的武器,
抽象外觀類的引入
外觀模式最大的缺點是違背了開閉原則,當增加新的子系統或移除子系統時需修改外觀類,可以通過引入抽象外觀類在一定程度上解決該問題,客戶端針對抽象外觀類編程,對于新的業務需求,不修改原有外觀類,對應增加一個新的具體外觀類,由新的具體外觀類來關聯新的子系統物件,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/45275.html
標籤:其他
上一篇:軟體設計模式修煉 -- 裝飾模式
