【設計模式】第十篇:外觀模式,開著小破車的快樂
阿新 • • 發佈:2020-12-20
![](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 類,來互動新舊系統