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

BWH_Steven發表於2020-12-20

一 開著小破車的快樂

不知道大家有沒有這樣開或者坐過這樣一輛“小破車”,他能跑,但是內部娛樂或者說一些輔助的裝置幾乎可以忽略不計,條件雖然艱苦了一些,但是我們還是要自己給自己創造快樂 ,夏天太熱了,先給自己安裝一臺空調,害,其實就是一臺小電扇,接著就是我們的 360度音響體驗了,其實也就是一個低音炮,來吧,最奢侈的一個裝置來了,遮光板上接一個螢幕,還能連一個簡單的 DVD 機器,好的吧,麻雀雖小,但是也算五臟俱全了,就像程式碼一樣,畢竟有功能,能執行的程式就是 “好程式” 對吧哈哈哈~

(一) 小破車的辛酸

上車,開啟我的小電扇,開啟小音響,再放好 DVD,就可以發動小破車出發了,下車的時候熄掉火,依次關掉 DVD,音響,電扇,就可以出去了,雖然滿滿的儀式感,但是因為這些外接的裝置都是一個一個獨立的,所以不管是開啟關閉,我都需要依次對其進行操作,自己忙了一天再回來在上折騰這個,別提多煩惱了

真羨慕別人的“豪華”小轎車,上車以後,一鍵打火,所有配套裝置自動啟動,涼颼颼的空調,動感的音樂

幻想著,我能不能也將我的小破車,改裝成 “智慧” 的模樣呢?

下面我們就用程式碼來看一下我們的小破車裝置改造

(二) 改造我的小破車

先復現一下我那些 DVD 、音響等等原來的狀態

說明:這裡只是為了演示,就在單執行緒環境下,簡單的用了餓漢式單例,空調也就是上面說的小電扇,姑且這麼叫好了

/**
 * 空調裝置
 */
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("關閉空調");
    }
}

這是音響

/**
 * 音響裝置
 */
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

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");
    }

}

如果使用傳統的方式測試一下

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 外觀類,將這些細節內容都封裝進去

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();
    }
}

再看看如何測試呢

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 提供給我們對外的一個方法,其實就是達到了一個封裝,精簡的效果

還有例子,例如在生活中要去辦戶口或者註冊公司等等,我們往往需要往返於多個部門之間,到處開證明,辦手續,但是如果有一個綜合性質的部門,統一辦理對應的業務,對於使用者來說就無須來回奔走,只需要根據這個綜合部分對外的視窗,提交指定的材料,等待其幫你辦理即可

再回到程式碼上,其實我們在平時的開發中已經有意或者無意的使用到了外觀模式,例如高層的模組中,我們想要呼叫多個相對複雜的子系統,我們為了精簡介面的數量,一般都會再建立一個新的類,對其進行呼叫封裝,然後使得最終呼叫者,可以更加簡潔容易的呼叫這些子系統的功能

(二) 結構

依舊分析一下其角色:

  • 外觀(Facade)角色:為多個子系統對外提供一個共同的介面或者說一致的介面,使得這些子系統更加好使用
  • 子系統(Sub System)角色:實現系統的部分功能,它們其實才是我們真正想要訪問的內容,客戶可以通過外觀角色訪問它
  • 客戶(Client)角色:通過一個外觀角色訪問各個子系統的功能

(三) 優缺點

(1) 優點

  • 簡化了呼叫過程:只需要訪問外觀模式給出的對外介面即可完成呼叫
  • 封裝性更好:使用外觀模式,子系統功能及具體細節被隱藏了起來,封裝性更好
  • 耦合性降低:呼叫者只與外觀物件進行互動,不直接與子系統進行接觸,降低了對子系統的依賴程式,降低了耦合
  • 符合迪米特法則

(2) 缺點

  • 不能很好的規避擴充套件風險:系統內部擴充套件子系統的時候,容易產生風險
  • 違背開閉原則:擴充套件子系統的時候,可能需要修改外觀類,會違背開閉原則

(四) 什麼時候使用外觀模式

(1) 層次複雜

我們在開發初期,會有意識的使用一些常見一些架構方式,例如 MVC 等等,在層級和業務很複雜的情況下,就需要考慮在每個層級都建立一個外觀物件作為入口,這樣就可以為複雜的子系統提供一些簡單的介面

(2) 子系統多且複雜

我們不斷的更新,擴充套件一些功能,就會導致有很多細小卻又缺不得的類,或者有一些非常複雜的系統,例如包含很多個類,這個時候我們可以考慮建立一個外觀 Facade 類,簡化介面,降低它們之間的依賴

(3) 呼叫老舊系統功能

有一些老舊的系統,幾乎已經沒有維護擴充套件的價值對了,但是其中又有一些牽扯很大的核心功能,我們在新系統中又沒有足夠的精力和成本去重構,只能設計一個外觀 Facade 類,來互動新舊系統的程式碼

相關文章