設計模式學習筆記九------外觀模式

目錄

本文的結構如下:

  • 什麼是外觀模式
  • 模式的結構
  • 代碼示例
  • 優點和缺點
  • 適用環境
  • 模式應用
  • 模式擴展
  • 補充

一、前言

話說某天空氣質量回到秦漢,月色如水傾瀉,溫度適宜,微風襲人,我在院子樹下架起圓木桌,擺上雕花凳,桌上依次擺放紅燒肘子,燒牛肉,剁椒魚頭,烤羊排,炸猛男……美人在懷,飲一口小酒,復又夾起一塊牛肉放入口中,這滋味,豈不快哉?

快你個頭啊,趕緊起來給我碼代碼。

恍惚中聽到一聲怒吼,我從好夢中驚醒。啊,沒有酒,沒有肉,沒有美人,只有漢城4點鐘的太陽,油膩的中年組長大叔和屏幕前沒有吃完的老壇酸菜面,淚哭。

咳咳,回到正題,我們接着講美食。美食適合享用,不宜動手操弄,如果自己動手,要做好一道美食,需要的事物很多,碗,鍋,水,調味品,食材等等,需要這些東西經過一些列交互才能得到最後的美食。

美食做法實在太過累人,可是還是想吃怎麼辦呢?

找媽媽啊,一句:“媽,我想吃肉。”然後可以逛知乎,刷微博,打遊戲,在手機上進行了一輪複雜操作,就聽媽媽喊一聲:“吃飯了。”於是乎,一隻手握着手機改看小說,一隻手夾肉,嘴裏嘟囔道:“媽,你做的紅燒肉太好吃了。”

咳咳,這次真的回到正題。

剛纔“餓了找媽媽”的例子其實就是在說外觀模式。在軟件開發中,有時候某個客戶類(“我”)可能會與多個業務類(“水,調味品,食材”)交互,而這些需要交互的業務類經常會作爲一個整體出現,由於涉及到的類比較多,導致使用時代碼較爲複雜,此時,特別需要一個類似“媽媽”一樣的角色,由它來負責和多個業務類進行交互,而客戶類只需與該類交互。外觀模式通過引入一個新的外觀類(Facade)來實現該功能,外觀類充當了軟件系統中的“媽媽”,它爲多個業務類的調用提供了一個統一的入口,簡化了類與類之間的交互。在外觀模式中,那些需要交互的業務類被稱爲子系統(Subsystem)。如果沒有外觀類,那麼每個客戶類需要和多個子系統之間進行復雜的交互,系統的耦合度將很大,而引入外觀類之後,客戶類只需要直接與外觀類交互,客戶類與子系統之間原有的複雜引用關係由外觀類來實現,從而降低了系統的耦合度。

插一句,不得不說,媽媽其實就像個服務員,不停服務自己的丈夫和孩子,還經常受到埋怨,太辛苦了。

二、什麼是外觀模式

爲子系統中的一組接口提供一個統一的入口。外觀模式定義了一個高層接口,這個接口使得這一子系統更加容易使用。

外觀模式又稱爲門面模式,它是一種對象結構型模式。外觀模式是迪米特法則的一種具體實現,通過引入一個新的外觀角色可以降低原有系統的複雜度,同時降低客戶類與子系統的耦合度。

三、模式的結構

外觀模式包含如下兩個角色:

  • Facade(外觀角色):在客戶端可以調用它的方法,在外觀角色中可以知道相關的(一個或者多個)子系統的功能和責任;在正常情況下,它將所有從客戶端發來的請求委派到相應的子系統去,傳遞給相應的子系統對象處理。
  • SubSystem(子系統角色):在軟件系統中可以有一個或者多個子系統角色,每一個子系統可以不是一個單獨的類,而是一個類的集合,它實現子系統的功能;每一個子系統都可以被客戶端直接調用,或者被外觀角色調用,它處理由外觀類傳過來的請求;子系統並不知道外觀的存在,對於子系統而言,外觀角色僅僅是另外一個客戶端而已。

外觀模式中所指的子系統是一個廣義的概念,它可以是一個類、一個功能模塊、系統的一個組成部分或者一個完整的系統。子系統類通常是一些業務類,實現了一些具體的、獨立的業務功能,其典型代碼:

class SubSystemA
{
    public void MethodA()
    {
        //業務實現代碼
    }
}

class SubSystemB
{
    public void MethodB()
    {
        //業務實現代碼
     }
}

class SubSystemC
{
    public void MethodC()
    {
        //業務實現代碼
    }
}

在引入外觀類之後,與子系統業務類之間的交互統一由外觀類來完成,在外觀類中通常存在如下代碼:

class Facade
{
    private SubSystemA obj1 = new SubSystemA();
    private SubSystemB obj2 = new SubSystemB();
    private SubSystemC obj3 = new SubSystemC();

    public void Method()
    {
        obj1.MethodA();
        obj2.MethodB();
        obj3.MethodC();
    }
}

由於在外觀類中維持了對子系統對象的引用,客戶端可以通過外觀類來間接調用子系統對象的業務方法,而無須與子系統對象直接交互。引入外觀類後,客戶端代碼變得非常簡單,典型代碼:

class Program
{
    static void Main(string[] args)
    {
        Facade facade = new Facade();
        facade.Method();
    }
}

四、代碼示例

4.1、就以“餓了找媽媽”爲例,在不使用外觀模式前,是這樣設計的:

/**
 * Created by w1992wishes on 2017/11/9.
 */
public class Material {
    public void prepare(){
        System.out.println("從菜市場買回食材,並清洗");
    }
}

/**
 * Created by w1992wishes on 2017/11/9.
 */
public class Pan {
    public void wash(){
        System.out.println("炒菜前要先洗鍋");
    }
}

/**
 * Created by w1992wishes on 2017/11/9.
 */
public class Flavouring {
    public void add(){
        System.out.println("依次放入各種調味品");
    }
}

我進行製作美食:

/**
 * Created by w1992wishes on 2017/11/9.
 */
public class Client {
    public static void main(String[] args) {
        Material material = new Material();
        Pan pan = new Pan();
        Flavouring flavouring = new Flavouring();

        System.out.println("肚子餓了");
        material.prepare();
        pan.wash();
        flavouring.add();
        System.out.println("出鍋,可以吃飯了");
    }
}

結果:
肚子餓了
從菜市場買回食材,並清洗
炒菜前要先洗鍋
依次放入各種調味品
出鍋,可以吃飯了

4.2、使用外觀模式

/**
 * Created by w1992wishes on 2017/11/9.
 */
public class Mom {
    Material material;
    Pan pan;
    Flavouring flavouring;
    public Mom(){
        this.material = new Material();
        this.pan = pan = new Pan();
        this.flavouring = new Flavouring();
    }
    public void makeDish(){
        material.prepare();
        pan.wash();
        flavouring.add();
    }
}

媽媽製作美食:

public class Client {
    public static void main(String[] args) {
        Mom mom = new Mom();

        System.out.println("肚子餓了");
        mom.makeDish();
        System.out.println("出鍋,可以吃飯了");
    }
}

五、優點和缺點

5.1、優點

  • 對客戶屏蔽子系統組件,減少了客戶處理的對象數目並使得子系統使用起來更加容易。通過引入外觀模式,客戶代碼將變得很簡單,與之關聯的對象也很少。
  • 實現了子系統與客戶之間的鬆耦合關係,這使得子系統的組件變化不會影響到調用它的客戶類,只需要調整外觀類即可。
  • 降低了大型軟件系統中的編譯依賴性,並簡化了系統在不同平臺之間的移植過程,因爲編譯一個子系統一般不需要編譯所有其他的子系統。一個子系統的修改對其他子系統沒有任何影響,而且子系統內部變化也不會影響到外觀對象。
  • 只是提供了一個訪問子系統的統一入口,並不影響用戶直接使用子系統類。

5.2、缺點

  • 不能很好地限制客戶使用子系統類,如果對客戶訪問子系統類做太多的限制則減少了可變性和靈活性。
  • 在不引入抽象外觀類的情況下,增加新的子系統可能需要修改外觀類或客戶端的源代碼,違背了“開閉原則”。

六、適用環境

在以下情況下可以使用外觀模式:

  • 當要爲一個複雜子系統提供一個簡單接口時可以使用外觀模式。該接口可以滿足大多數用戶的需求,而且用戶也可以越過外觀類直接訪問子系統。
  • 客戶程序與多個子系統之間存在很大的依賴性。引入外觀類將子系統與客戶以及其他子系統解耦,可以提高子系統的獨立性和可移植性。
  • 在層次化結構中,可以使用外觀模式定義系統中每一層的入口,層與層之間不直接產生聯繫,而通過外觀類建立聯繫,降低層之間的耦合度。

七、模式應用

八、模式擴展

  • 一個系統可以有多個外觀類:在外觀模式中,通常只需要一個外觀類,並且此外觀類只有一個實例,換言之它是一個單例類。在很多情況下爲了節約系統資源,一般將外觀類設計爲單例類。當然這並不意味着在整個系統裏只能有一個外觀類,在一個系統中可以設計多個外觀類,每個外觀類都負責和一些特定的子系統交互,向用戶提供相應的業務功能。
  • 不要試圖通過外觀類爲子系統增加新行爲:不要通過繼承一個外觀類在子系統中加入新的行爲,這種做法是錯誤的。外觀模式的用意是爲子系統提供一個集中化和簡化的溝通渠道,而不是向子系統加入新的行爲,新的行爲的增加應該通過修改原有子系統類或增加新的子系統類來實現,不能通過外觀類來實現。
  • 外觀模式與迪米特法則:外觀模式創造出一個外觀對象,將客戶端所涉及的屬於一個子系統的協作夥伴的數量減到最少,使得客戶端與子系統內部的對象的相互作用被外觀對象所取代。外觀類充當了客戶類與子系統類之間的“第三者”,降低了客戶類與子系統類之間的耦合度,外觀模式就是實現代碼重構以便達到“迪米特法則”要求的一個強有力的武器。
  • 抽象外觀類的引入:在標準的外觀模式結構圖中,如果需要增加、刪除或更換與外觀類交互的子系統類,必須修改外觀類或客戶端的源代碼,這將違背開閉原則,因此可以通過引入抽象外觀類來對系統進行改進,在一定程度上可以解決該問題。在引入抽象外觀類之後,客戶端可以針對抽象外觀類進行編程,對於新的業務需求,不需要修改原有外觀類,而對應增加一個新的具體外觀類,由新的具體外觀類來關聯新的子系統對象,同時通過修改配置文件來達到不修改任何源代碼並更換外觀類的目的。

九、補充–與適配器模式的區別

  • 外觀模式的實現核心主要是——由外觀類去保存各個子系統的引用,實現由一個統一的外觀類去包裝多個子系統類,然後客戶端只需要引用這個外觀類,然後由外觀類來調用各個子系統中的方法。
  • 這樣的實現方式非常類似適配器模式,然而外觀模式與適配器模式不同的是:適配器模式是將一個對象包裝起來以改變其接口,而外觀是將一羣對象 ”包裝“起來以簡化其接口。它們的意圖是不一樣的,適配器是將接口轉換爲不同接口,而外觀模式是提供一個統一的接口來簡化接口。

--------------------- 本文來自 w1992wishes 的CSDN 博客 ,全文地址請點擊:https://blog.csdn.net/w1992wishes/article/details/78491346?utm_source=copy

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章