設計模式(10) 門面/外觀模式(簡單入門 結構模式)

From Now On,Let us begin Design Patterns。

門面模式, 也稱外觀模式

定義

  • 要求一個子系統的外部與其內部的通信必須通過一個統一的對象進行。門面模式提供一個高層次的接口,使得子系統更易於使用。)簡言之,門面對象是外界訪問子系統內部的唯一通道。 (Façade Pattern)Provide a unified interface to a set of interfaces in a subsystem. Façade defines a higher-level interface that makes the subsystem easier to use.

通用類圖:
這裏寫圖片描述

角色說明:
門面角色: 客戶端可以調用這個角色的方法。它知曉子系統的所有功能和責任。該角色沒有實際的業務邏輯,只是一個委託類,將客戶端發來的請求委派到相應的子系統去。同時只給客戶端暴露客戶端需要的方法,屏蔽他們用不到的方法。

子系統角色:可以同時有一個或多個子系統。每個子系統都是一個類的集合。子系統不知道門面的存在,門面相對於子系統僅是另外一個客戶端而已。

適配器通用代碼:
這裏寫圖片描述

門面模式優點:

  • 減少系統的相互依賴:把外界對子系統的依賴轉爲對門面的依賴,與子系統無關

  • 鬆散耦合:門面模式鬆散了客戶端與子系統的耦合關係,讓子系統內部的模塊能更容易擴展和維護

  • 提高了靈活性:子系統作變化,只要不影響到門面對象即可

  • 簡單易用:門面模式讓子系統更加易用,客戶端不再需要了解子系統內部的實現,也不需要跟衆多子系統內部的模塊進行交互,只需要跟門面類交互就可以了

  • 門面模式提高了安全性:外界只能訪問到門面上開通的方法,而不能觸及子系統的其它業務

  • 更好的劃分訪問層次:通過合理使用Facade,可以幫助我們更好地劃分訪問的層次。有些方法是對系統外的,有些方法是系統內部使用的。把需要暴露給外部的功能集中到門面中,這樣既方便客戶端使用,也很好地隱藏了內部的細節

門面模式的缺點:

  • 不符合開閉原則,一旦在系統投產後發現錯誤,就只能修改門面角色的代碼,風險大需考慮,門面類很容易就成了萬能類

門面模式的使用場景:

爲一個複雜的模塊或子系統提供一個供外界訪問的接口

子系統相對立——外界對子系統的訪問只要黑箱操作即可

預防低水平人員開帶來的風險擴散(爲其限定某一子系統,供其開發)

不想對外界暴露過多的方法,只暴露客戶端用到的的方法

注意事項:

1:一個子系統可以有多個門面

門面已經在到不能忍受的程度(比如一個門面對象已經超過了200行代碼),雖然都是非常簡單的委託操作,也建議拆分成多個門面,否則會給以後的維護和擴展帶來不必要的麻煩。如何拆分?可以按功能:如數據庫操作的門面可以分爲查詢門面、刪除門面、更新門面等。

子系統可以提供不同訪問路徑:以通用源碼爲例,ClassA、ClassB、ClassC是一個子系統中的3個對象,現在有兩個不同的高層模塊來訪問該子系統,模塊一是子系統的信任模塊,可以完整的訪問子系統的所有業務邏輯;而模塊二屬於受限訪問對象,只能訪問methodB方法。此時,就需要建立兩個門面以供不同的高層模塊來訪問,即在原有代碼中增加一個門面即可

public class Facade2 {
    //引用原有的門面
    private Façade façade = new Façade();
    //對外提供唯一的訪問子系統的方法
    public void methodB(){
        This.facade.methodB();
    }
}

爲什麼要使用委託而不再編寫一個委託到子系統的方法呢?那是因爲在面向對象的編程中,儘量保持相同的代碼只編寫一遍,避免以後到處修改相似代碼的悲劇。

2. 門面不參與子系統內的業務邏輯——-不參與子系統業務邏輯

以通用代碼爲例:如果methodC中必須先調用ClassA的doSomethingA,然後再調用 ClassC 的doSomethingC方法呢?如何改。但千萬別這麼做:

public void methodC( ) {
    this.a.doSomethingA();
    this.c.doSomethingC();
}

爲什麼不要這麼做呢?因爲你已經讓門面對象參與了業務邏輯,而門面對象只提供了個訪問子系統的一個路徑而已。否則就會產生一個倒依賴的問題:子系統必須依賴門面才能被訪問,門面就會跟子系統冗雜在一塊了,這是設計上的一個嚴重錯誤,不僅違反了單一職責原則,同時也破壞了系統的封裝性。

解決之道———繼續抽象封裝:建立一個封裝類,封裝完畢後提供給門面對象。

public class Context {
    // 委託處理
    private ClassA a = new ClassA();
    private ClassC c = new ClassC();

    // 複雜的計算
    public void complexMethod() {
        this.a.doSomethingA();
        this.c.doSomethingC();
    }
}

public class Facade {
    // 被委託的對象
    private ClassA a = new ClassA();
    private ClassB b = new ClassB();
    private Context context = new Context();

    // 提供給外部訪問的方法
    public void methodA() {
        this.a.doSomethingA();
    }

    public void methodB() {
        this.b.doSomethingB();
    }

    public void methodC() {
        this.context.complexMethod();
    }
}

門面模式的例子:源自《設計模式之禪》

類圖:
這裏寫圖片描述

系統模塊A B C:
這裏寫圖片描述

門面類
這裏寫圖片描述

這樣定義一個ModuleFacade類可以有效地屏蔽內部的細節,免得客戶端去調用Module類時,發現一些不需要它知道的方法。比如a2()和a3()方法就不需要讓客戶端知道,否則既暴露了內部的細節,又讓客戶端迷惑。對客戶端來說,他可能還要去思考a2()、a3()方法用來幹什麼呢?其實a2()和a3()方法是內部模塊之間交互的,原本就不是對子系統外部的,所以乾脆就不要讓客戶端知道。

一個系統可以有幾個門面類(錯誤的)
  在門面模式中,通常只需要一個門面類,並且此門面類只有一個實例,換言之它是一個單例類。當然這並不意味着在整個系統裏只有一個門面類,而僅僅是說對每一個子系統只有一個門面類。或者說,如果一個系統有好幾個子系統的話,每一個子系統都有一個門面類,整個系統可以有數個門面類。

爲子系統增加新行爲(錯誤的)
  初學者往往以爲通過繼承一個門面類便可在子系統中加入新的行爲,這是錯誤的。門面模式的用意是爲子系統提供一個集中化和簡化的溝通管道,而不能向子系統加入新的行爲。比如醫院中的接待員並不是醫護人員,接待員並不能爲病人提供醫療服務。

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