門面模式的定義
門面模式(Facade Pattern)也叫做外觀模式,是一種比較常用的封裝模式,其定義如
下:
Provide a unified interface to a set of interfaces in a subsystem.Facade defines a higher-level
interface that makes the subsystem easier to use.(要求一個子系統的外部與其內部的通信必須通
過一個統一的對象進行。門面模式提供一個高層次的接口,使得子系統更易于使用。)
門面模式注重“統一的對象”,也就是提供一個訪問子系統的接口,除了這個接口不允許
有任何訪問子系統的行為發生
?
● Facade門面角色
?
客戶端可以調用這個角色的方法。此角色知曉子系統的所有功能和責任。一般情況下,
?
本角色會將所有從客戶端發來的請求委派到相應的子系統去,也就說該角色沒有實際的業務
?
邏輯,只是一個委托類。
?
?
?
● subsystem子系統角色
?
可以同時有一個或者多個子系統。每一個子系統都不是一個單獨的類,而是一個類的集
?
合。子系統并不知道門面的存在。對于子系統而言,門面僅僅是另外一個客戶端而已。
?
?子系統
?
public class ClassA {
?
?????public void doSomethingA(){
?
?????????????//業務邏輯
?
?????}
?
}
?
public class ClassB {
?
?????
?
?????public void doSomethingB(){
?
?????????????//業務邏輯
?
?????}
?
}
?
public class ClassC {
?
?????
?
?????public void doSomethingC(){
?
?????????????//業務邏輯
?
?????}
?
}
門面對象
public class Facade {
?????//被委托的對象
?????private ClassA a = new ClassA();
?????private ClassB b = new ClassB();
?????private ClassC c = new ClassC();
?????//提供給外部訪問的方法
?????public void methodA(){
?????????????this.a.doSomethingA();
?????}
?????
?????public void methodB(){
?
?????????????this.b.doSomethingB();
?????}
?????
?????public void methodC(){
?????????????this.c.doSomethingC();
?????}
}
?
門面模式的優點
門面模式有如下優點。
● 減少系統的相互依賴
如果我們不使用門面模式,外界訪問直接深入到子系統內部,相互之間是一種
強耦合關系,你死我就死,你活我才能活,這樣的強依賴是系統設計所不能接受的,門面模
式的出現就很好地解決了該問題,所有的依賴都是對門面對象的依賴,與子系統無關。
● 提高了靈活性
依賴減少了,靈活性自然提高了。不管子系統內部如何變化,只要不影響到門面對象,
任你自由活動。
● 提高安全性
想讓你訪問子系統的哪些業務就開通哪些邏輯,不在門面上開通的方法,你休想訪問
到。
?
門面模式的缺點
?
門面模式最大的缺點就是不符合開閉原則,對修改關閉,對擴展開放
?
門面模式的使用場景
?
● 為一個復雜的模塊或子系統提供一個供外界訪問的接口
?
● 子系統相對獨立——外界對子系統的訪問只要黑箱操作即可
● 預防低水平人員帶來的風險擴散
?
?
門面模式的注意事項
?
1 一個子系統可以有多個門面
?
● 門面已經龐大到不能忍受的程度
● 子系統可以提供不同訪問路徑
我們以門面模式的通用源代碼為例。ClassA、ClassB、ClassC是一個子系統的中3個對
象,現在有兩個不同的高層模塊來訪問該子系統,模塊一可以完整的訪問所有業務邏輯,也
就是通用代碼中的Facade類,它是子系統的信任模塊;而模塊二屬于受限訪問對象,只能訪
問methodB方法,那該如何處理呢?在這種情況下,就需要建立兩個門面以供不同的高層模
塊來訪問,在原有的通用源碼上增加一個新的門面即可
新增門面
public class Facade2 {
?????//引用原有的門面
?????private Facade facade = new Facade();
?????//對外提供唯一的訪問子系統的方法
?????public void methodB(){
?????????????this.facade.methodB();
?????}
}
增加的門面非常簡單,委托給了已經存在的門面對象Facade進行處理,為什么要使用委
托而不再編寫一個委托到子系統的方法呢?那是因為在面向對象的編程中,盡量保持相同的
代碼只編寫一遍,避免以后到處修改相似代碼的悲劇。
?
2 門面不參與子系統內的業務邏輯
我們
把門面上的methodC上的邏輯修改一下,它必須先調用ClassA的doSomethingA方法,然后再
調用ClassC的doSomethingC方法
?
修改門面
?
public class Facade {
?
?????//被委托的對象
?
?????private ClassA a = new ClassA();
?
?????private ClassB b = new ClassB();
?
?????private ClassC c = new ClassC();
?
?????//提供給外部訪問的方法
?
?????public void methodA(){
?
?????????????this.a.doSomethingA();
?
?????}
?
?????
?
?????public void methodB(){
?
?????????????this.b.doSomethingB();
?
?????}
?
?????
?
?????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();
?
?????}
?
}
?
該封裝類的作用就是產生一個業務規則complexMethod,并且它的生存環境是在子系統
?
內,僅僅依賴兩個相關的對象,門面對象通過對它的訪問完成一個復雜的業務邏輯
?
門面類
?
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();
?
?????}
?
}
?
通過這樣一次封裝后,門面對象又不參與業務邏輯了,在門面模式中,門面角色應該是
?
穩定,它不應該經常變化,一個系統一旦投入運行它就不應該被改變,它是一個系統對外的
?
接口,你變來變去還怎么保證其他模塊的穩定運行呢?但是,業務邏輯是會經常變化的,我
?
們已經把它的變化封裝在子系統內部,無論你如何變化,對外界的訪問者來說,都還是同一
?
個門面,同樣的方法——這才是架構師最希望看到的結構。
?