1. 程式人生 > >【設計模式】第十篇:外觀模式,開著小破車的快樂

【設計模式】第十篇:外觀模式,開著小破車的快樂

![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6cc3b1afe0eb425197976764db0d545c~tplv-k3u1fbpfcp-zoom-1.image) # 一 開著小破車的快樂 不知道大家有沒有這樣開或者坐過這樣一輛“小破車”,他能跑,但是內部娛樂或者說一些輔助的裝置幾乎可以忽略不計,條件雖然艱苦了一些,但是我們還是要自己給自己創造快樂 ,夏天太熱了,先給自己安裝一臺空調,害,其實就是一臺小電扇,接著就是我們的 360度音響體驗了,其實也就是一個低音炮,來吧,最奢侈的一個裝置來了,遮光板上接一個螢幕,還能連一個簡單的 DVD 機器,好的吧,麻雀雖小,但是也算五臟俱全了,就像程式碼一樣,畢竟有功能,能執行的程式就是 “好程式” 對吧哈哈哈~ ## (一) 小破車的辛酸 上車,開啟我的小電扇,開啟小音響,再放好 DVD,就可以發動小破車出發了,下車的時候熄掉火,依次關掉 DVD,音響,電扇,就可以出去了,雖然滿滿的儀式感,但是因為這些外接的裝置都是一個一個獨立的,所以不管是開啟關閉,我都需要依次對其進行操作,自己忙了一天再回來在上折騰這個,別提多煩惱了 真羨慕別人的“豪華”小轎車,上車以後,一鍵打火,所有配套裝置自動啟動,涼颼颼的空調,動感的音樂 幻想著,我能不能也將我的小破車,改裝成 “智慧” 的模樣呢? 下面我們就用程式碼來看一下我們的小破車裝置改造 ## (二) 改造我的小破車 先復現一下我那些 DVD 、音響等等原來的狀態 說明:這裡只是為了演示,就在單執行緒環境下,簡單的用了餓漢式單例,空調也就是上面說的小電扇,姑且這麼叫好了 ```java /** * 空調裝置 */ public class AirConditioner { // 餓漢式單例 private static AirConditioner instance = new AirConditioner(); public static AirConditioner getInstance() { return instance; } public void turnOn() { System.out.println("開啟空調"); } public void turnOff() { System.out.println("關閉空調"); } } ``` 這是音響 ```java /** * 音響裝置 */ public class Sound { // 餓漢式單例 private static Sound instance = new Sound(); public static Sound getInstance() { return instance; } public void turnOn() { System.out.println("開啟音響"); } public void turnOff() { System.out.println("關閉音響"); } } ``` 這是 DVD ```java public class DVDPlayer { // 餓漢式單例 private static DVDPlayer instance = new DVDPlayer(); public static DVDPlayer getInstance() { return instance; } public void turnOn() { System.out.println("開啟DVD"); } public void turnOff() { System.out.println("關閉DVD"); } } ``` 如果使用傳統的方式測試一下 ```java public class Test { public static void main(String[] args) { // 拿到三種裝置的例項 AirConditioner airConditioner = AirConditioner.getInstance(); DVDPlayer dvdPlayer = DVDPlayer.getInstance(); Sound sound = Sound.getInstance(); System.out.println("=====開啟的過程====="); airConditioner.turnOn(); dvdPlayer.turnOn(); sound.turnOn(); System.out.println("=====關閉的過程====="); airConditioner.turnOff(); dvdPlayer.turnOff(); sound.turnOff(); } } ``` 測試結果 ``` =====開啟的過程===== 開啟空調 開啟DVD 開啟音響 =====關閉的過程===== 關閉空調 關閉DVD 關閉音響 ``` 效果沒問題了,但是可以看出來,只有短短三臺裝置的開關就需要執行 6 個方法,如果裝置更多一些,如果操作不僅只有開關,還有一些別的,豈不是要累死,雖然咱的車是破舊了一些,但這也太折騰了 來吧,改造! 我們建立一個 CarFade 外觀類,將這些細節內容都封裝進去 ```java public class CarFacade { private AirConditioner airConditioner; private DVDPlayer dvdPlayer; private Sound sound; // 在無參構造中拿到例項 public CarFacade() { this.airConditioner = AirConditioner.getInstance(); this.dvdPlayer = DVDPlayer.getInstance(); this.sound = Sound.getInstance(); } // 一鍵開啟 public void turnOn() { airConditioner.turnOn(); dvdPlayer.turnOn(); sound.turnOn(); } // 一鍵關閉 public void turnOff() { airConditioner.turnOff(); dvdPlayer.turnOff(); sound.turnOff(); } } ``` 再看看如何測試呢 ```java package cn.ideal.facade; /** * @ClassName: Test * @Author: BWH_Steven * @Date: 2020/11/27 11:35 * @Version: 1.0 */ public class Test { public static void main(String[] args) { // 拿到三種裝置的例項 CarFacade carFacade = new CarFacade(); System.out.println("=====開啟的過程====="); carFacade.turnOn(); System.out.println("=====關閉的過程====="); carFacade.turnOff(); } } ``` 測試結果: ``` =====開啟的過程===== 開啟空調 開啟DVD 開啟音響 =====關閉的過程===== 關閉空調 關閉DVD 關閉音響 ``` 效果一樣沒問題,但是我們作為呼叫者,這可舒服了,我們也可以一鍵開關這些娛樂輔助裝置了,其實這就是利用了一種簡單實用的設計模式——外觀模式,下面來一起看看它的概念 # 二 外觀模式理論 ## (一) 概念 **外觀模式(門面模式):它是一種通過為多個複雜的子系統提供一個一致的介面,而使這些子系統更加容易被訪問的模式** 就著上面的例子也很好理解,空調、印象、DVD,就是一個一個複雜的子系統,而我們為這幾者,提供一個一致的 CarFacade ,我們就避免去訪問一個一個子系統的具體細節,而只需要執行,這個 CarFacade 提供給我們對外的一個方法,其實就是達到了一個封裝,精簡的效果 還有例子,例如在生活中要去辦戶口或者註冊公司等等,我們往往需要往返於多個部門之間,到處開證明,辦手續,但是如果有一個綜合性質的部門,統一辦理對應的業務,對於使用者來說就無須來回奔走,只需要根據這個綜合部分對外的視窗,提交指定的材料,等待其幫你辦理即可 再回到程式碼上,其實我們在平時的開發中已經有意或者無意的使用到了外觀模式,例如高層的模組中,我們想要呼叫多個相對複雜的子系統,我們為了**精簡介面的數量**,一般都會再建立一個新的類,對其進行呼叫封裝,然後使得最終呼叫者,可以更加簡潔容易的呼叫這些子系統的功能 ## (二) 結構 ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2fb821486e004fa3ba6a6140f3eb110b~tplv-k3u1fbpfcp-zoom-1.image) 依舊分析一下其角色: - 外觀(Facade)角色:為多個子系統對外提供一個共同的介面或者說一致的介面,使得這些子系統更加好使用 - 子系統(Sub System)角色:實現系統的部分功能,它們其實才是我們真正想要訪問的內容,客戶可以通過外觀角色訪問它 - 客戶(Client)角色:通過一個外觀角色訪問各個子系統的功能 ## (三) 優缺點 ### (1) 優點 - **簡化了呼叫過程**:只需要訪問外觀模式給出的對外介面即可完成呼叫 - **封裝性更好**:使用外觀模式,子系統功能及具體細節被隱藏了起來,封裝性更好 - **耦合性降低**:呼叫者只與外觀物件進行互動,不直接與子系統進行接觸,降低了對子系統的依賴程式,降低了耦合 - **符合迪米特法則** ### (2) 缺點 - **不能很好的規避擴充套件風險**:系統內部擴充套件子系統的時候,容易產生風險 - **違背開閉原則**:擴充套件子系統的時候,可能需要修改外觀類,會違背開閉原則 ## (四) 什麼時候使用外觀模式 ### (1) 層次複雜 我們在開發初期,會有意識的使用一些常見一些架構方式,例如 MVC 等等,在層級和業務很複雜的情況下,就需要考慮在每個層級都建立一個外觀物件作為入口,這樣就可以為複雜的子系統提供一些簡單的介面 ### (2) 子系統多且複雜 我們不斷的更新,擴充套件一些功能,就會導致有很多細小卻又缺不得的類,或者有一些非常複雜的系統,例如包含很多個類,這個時候我們可以考慮建立一個外觀 Facade 類,簡化介面,降低它們之間的依賴 ### (3) 呼叫老舊系統功能 有一些老舊的系統,幾乎已經沒有維護擴充套件的價值對了,但是其中又有一些牽扯很大的核心功能,我們在新系統中又沒有足夠的精力和成本去重構,只能設計一個外觀 Facade 類,來互動新舊系統