簡介
外觀模式(Facade Pattern)是一種設計模式,又名門面模式,是一種通過為多個復雜的子系統提供一個一致的接口,而使這些子系統更加容易被訪問的模式。該模式對外有一個統一接口,外部應用程序不用關心內部子系統的具體的細節,這樣會大大降低應用程序的復雜度,提高了程序的可維護性。
外觀模式的關鍵在于隱藏系統的復雜性,并簡化客戶端與系統之間的交互。它通過提供一個統一的接口,使得客戶端無需了解系統內部的復雜聯系和實現細節,只需要通過外觀類與系統進行交互即可。這有助于降低系統的耦合度,提高系統的可維護性和可擴展性。
外觀(Facade)模式是“迪米特法則”的典型應用
結構
-
外觀(Facade)模式包含以下主要角色:
- 外觀(Facade)角色:為多個子系統對外提供一個共同的接口。
- 子系統(Sub System)角色:實現系統的部分功能,客戶可以通過外觀角色訪問它。
案例實現
【例】智能家電控制
小明的爺爺已經60歲了,一個人在家生活:每次都需要打開燈、打開電視、打開空調;睡覺時關閉燈、關閉電視、關閉空調;操作起來都比較麻煩。所以小明給爺爺買了智能音箱,可以通過語音直接控制這些智能家電的開啟和關閉。類圖如下:
代碼如下:
//燈類
public class Light {public void on() {System.out.println("打開了燈....");}public void off() {System.out.println("關閉了燈....");}
}//電視類
public class TV {public void on() {System.out.println("打開了電視....");}public void off() {System.out.println("關閉了電視....");}
}//控制類
public class AirCondition {public void on() {System.out.println("打開了空調....");}public void off() {System.out.println("關閉了空調....");}
}//智能音箱
public class SmartAppliancesFacade {private Light light;private TV tv;private AirCondition airCondition;public SmartAppliancesFacade() {light = new Light();tv = new TV();airCondition = new AirCondition();}public void say(String message) {if(message.contains("打開")) {on();} else if(message.contains("關閉")) {off();} else {System.out.println("我還聽不懂你說的!!!");}}//起床后一鍵開電器private void on() {System.out.println("起床了");light.on();tv.on();airCondition.on();}//睡覺一鍵關電器private void off() {System.out.println("睡覺了");light.off();tv.off();airCondition.off();}
}//測試類
public class Client {public static void main(String[] args) {//創建外觀對象SmartAppliancesFacade facade = new SmartAppliancesFacade();//客戶端直接與外觀對象進行交互facade.say("打開家電");facade.say("關閉家電");}
}
優缺點
外觀模式的優點包括:
- 降低系統耦合度:通過提供一個統一的接口,將客戶端與系統的具體實現細節解耦,使得系統更加靈活和可維護。
- 提高安全性:通過限制對子系統的直接訪問,可以預防低水平人員帶來的風險,保護系統的安全。
- 提高可擴展性:通過將復雜的子系統隱藏在外觀類后面,可以更容易地添加新的子系統和功能,提高系統的可擴展性。
然而,外觀模式也存在一些缺點:
- 不符合開閉原則:如果要修改子系統中的某些方法,可能需要修改外觀類,這可能會違反開閉原則。
- 增加了系統的層次:使用外觀模式會增加系統的層次,這可能會影響系統的性能和可讀性。
使用場景
- 對分層結構系統構建時,使用外觀模式定義子系統中每層的入口點可以簡化子系統之間的依賴關系。
- 當一個復雜系統的子系統很多時,外觀模式可以為系統設計一個簡單的接口供外界訪問。
- 當客戶端與多個子系統之間存在很大的聯系時,引入外觀模式可將它們分離,從而提高子系統的獨立性和可移植性。
源碼中的應用
Tomcat
RequestFacade
使用tomcat作為web容器時,接收瀏覽器發送過來的請求,tomcat會將請求信息封裝成ServletRequest對象,如下圖①處對象。但是大家想想ServletRequest是一個接口,它還有一個子接口HttpServletRequest,而我們知道該request對象肯定是一個HttpServletRequest對象的子實現類對象,到底是哪個類的對象呢?可以通過輸出request對象,我們就會發現是一個名為RequestFacade的類的對象。
RequestFacade類就使用了外觀模式。先看結構圖:
為什么在此處使用外觀模式呢?
? 定義 RequestFacade 類,分別實現 ServletRequest ,同時定義私有成員變量 Request ,并且方法的實現調用 Request 的實現。然后,將 RequestFacade上轉為 ServletRequest 傳給 servlet 的 service 方法,這樣即使在 servlet 中被下轉為 RequestFacade ,也不能訪問私有成員變量對象中的方法。既用了 Request ,又能防止其中方法被不合理的訪問。
Spring
ApplicationContext
ApplicationContext
是Spring框架中的一個核心接口,它提供了配置信息以及訪問定義好的bean的功能。ApplicationContext
可以看作是Spring容器的一個外觀類,它封裝了底層BeanFactory的復雜性,為開發者提供了一個更加高級和易于使用的接口。通過ApplicationContext
,開發者可以輕松地獲取bean的實例,而無需直接與底層的BeanFactory進行交互。