10赦肋、結構型模式之外觀模式(門面模式)

在閻宏博士的《JAVA與模式》一書中開頭是這樣描述門面(Facade)模式的:

門面模式(外觀模式)是對象的結構模式撇贺,外部與一個子系統(tǒng)的通信必須通過一個統(tǒng)一的門面對象進行兵迅。門面模式提供一個高層次的接口,使得子系統(tǒng)更易于使用例衍。


醫(yī)院的例子

現(xiàn)代的軟件系統(tǒng)都是比較復雜的昔期,設計師處理復雜系統(tǒng)的一個常見方法便是將其“分而治之”,把一個系統(tǒng)劃分為幾個較小的子系統(tǒng)佛玄。如果把醫(yī)院作為一個子系統(tǒng)硼一,按照部門職能,這個系統(tǒng)可以劃分為掛號梦抢、門診般贼、劃價、化驗奥吩、收費哼蛆、取藥等∪ν眨看病的病人要與這些部門打交道人芽,就如同一個子系統(tǒng)的客戶端與一個子系統(tǒng)的各個類打交道一樣,不是一件容易的事情绩脆。

首先病人必須先掛號萤厅,然后門診橄抹。如果醫(yī)生要求化驗,病人必須首先劃價惕味,然后繳費楼誓,才可以到化驗部門做化驗∶樱化驗后再回到門診室疟羹。

image.png

上圖描述的是病人在醫(yī)院里的體驗,圖中的方框代表醫(yī)院禀倔。

解決這種不便的方法便是引進門面模式榄融,醫(yī)院可以設置一個接待員的位置,由接待員負責代為掛號救湖、劃價愧杯、繳費拜秧、取藥等蒲每。這個接待員就是門面模式的體現(xiàn)澎迎,病人只接觸接待員葵腹,由接待員與各個部門打交道。

image.png

門面模式的結構

門面模式?jīng)]有一個一般化的類圖描述方库,最好的描述方法實際上就是以一個例子說明钮糖。

image.png

由于門面模式的結構圖過于抽象宴猾,因此把它稍稍具體點陡舅。假設子系統(tǒng)內(nèi)有三個模塊抵乓,分別是ModuleA、ModuleB和ModuleC蹭沛,它們分別有一個示例方法臂寝,那么此時示例的整體結構圖如下:

image.png

在這個對象圖中章鲤,出現(xiàn)了兩個角色:

●  門面(Facade)角色 :客戶端可以調(diào)用這個角色的方法摊灭。此角色知曉相關的(一個或者多個)子系統(tǒng)的功能和責任。在正常情況下败徊,本角色會將所有從客戶端發(fā)來的請求委派到相應的子系統(tǒng)去帚呼。

●  子系統(tǒng)(SubSystem)角色 :可以同時有一個或者多個子系統(tǒng)。每個子系統(tǒng)都不是一個單獨的類皱蹦,而是一個類的集合(如上面的子系統(tǒng)就是由ModuleA煤杀、ModuleB、ModuleC三個類組合而成)沪哺。每個子系統(tǒng)都可以被客戶端直接調(diào)用沈自,或者被門面角色調(diào)用。子系統(tǒng)并不知道門面的存在辜妓,對于子系統(tǒng)而言枯途,門面僅僅是另外一個客戶端而已忌怎。

源代碼

子系統(tǒng)角色中的類:

package com.nieshenkuan.constitutive.facade;

public class ModuleA {
    // 示意方法
    public void testA() {
        System.out.println("調(diào)用ModuleA中的testA方法");
    }
}
package com.nieshenkuan.constitutive.facade;

public class ModuleB {
    // 示意方法
    public void testB() {
        System.out.println("調(diào)用ModuleB中的testB方法");
    }
}
package com.nieshenkuan.constitutive.facade;

public class ModuleC {
    // 示意方法
    public void testC() {
        System.out.println("調(diào)用ModuleC中的testC方法");
    }
}

門面角色類:

package com.nieshenkuan.constitutive.facade;

public class Facade {
    // 示意方法,滿足客戶端需要的功能
    public void test() {
        ModuleA a = new ModuleA();
        a.testA();
        ModuleB b = new ModuleB();
        b.testB();
        ModuleC c = new ModuleC();
        c.testC();
    }
}

客戶端角色類:

package com.nieshenkuan.constitutive.facade;

public class Client {

    public static void main(String[] args) {

        Facade facade = new Facade();
        facade.test();
    }
}

結果:

調(diào)用ModuleA中的testA方法
調(diào)用ModuleB中的testB方法
調(diào)用ModuleC中的testC方法

Facade類其實相當于A酪夷、B榴啸、C模塊的外觀界面,有了這個Facade類晚岭,那么客戶端就不需要親自調(diào)用子系統(tǒng)中的A鸥印、B、C模塊了坦报,也不需要知道系統(tǒng)內(nèi)部的實現(xiàn)細節(jié)库说,甚至都不需要知道A、B片择、C模塊的存在璃弄,客戶端只需要跟Facade類交互就好了,從而更好地實現(xiàn)了客戶端和子系統(tǒng)中A构回、B夏块、C模塊的解耦,讓客戶端更容易地使用系統(tǒng)纤掸。


門面模式的實現(xiàn)

使用門面模式還有一個附帶的好處脐供,就是能夠有選擇性地暴露方法。一個模塊中定義的方法可以分成兩部分借跪,一部分是給子系統(tǒng)外部使用的政己,一部分是子系統(tǒng)內(nèi)部模塊之間相互調(diào)用時使用的。有了Facade類掏愁,那么用于子系統(tǒng)內(nèi)部模塊之間相互調(diào)用的方法就不用暴露給子系統(tǒng)外部了歇由。

比如,定義如下A果港、B沦泌、C模塊。

public class ModuleA {
    /**
     * 提供給子系統(tǒng)外部使用的方法
     */
    public void a1(){};
    
    /**
     * 子系統(tǒng)內(nèi)部模塊之間相互調(diào)用時使用的方法
     */
    public void a2(){};
    public void a3(){};
}
public class ModuleB {
    /**
     * 提供給子系統(tǒng)外部使用的方法
     */
    public void b1(){};
    
    /**
     * 子系統(tǒng)內(nèi)部模塊之間相互調(diào)用時使用的方法
     */
    public void b2(){};
    public void b3(){};
}
public class ModuleC {
    /**
     * 提供給子系統(tǒng)外部使用的方法
     */
    public void c1(){};
    
    /**
     * 子系統(tǒng)內(nèi)部模塊之間相互調(diào)用時使用的方法
     */
    public void c2(){};
    public void c3(){};
}
public class ModuleFacade {
    
    ModuleA a = new ModuleA();
    ModuleB b = new ModuleB();
    ModuleC c = new ModuleC();
    /**
     * 下面這些是A辛掠、B谢谦、C模塊對子系統(tǒng)外部提供的方法
     */
    public void a1(){
        a.a1();
    }
    public void b1(){
        b.b1();
    }
    public void c1(){
        c.c1();
    }
}

這樣定義一個ModuleFacade類可以有效地屏蔽內(nèi)部的細節(jié),免得客戶端去調(diào)用Module類時萝衩,發(fā)現(xiàn)一些不需要它知道的方法回挽。比如a2()和a3()方法就不需要讓客戶端知道,否則既暴露了內(nèi)部的細節(jié)猩谊,又讓客戶端迷惑千劈。對客戶端來說,他可能還要去思考a2()牌捷、a3()方法用來干什么呢墙牌?其實a2()和a3()方法是內(nèi)部模塊之間交互的袁梗,原本就不是對子系統(tǒng)外部的,所以干脆就不要讓客戶端知道憔古。

一個系統(tǒng)可以有幾個門面類

在門面模式中遮怜,通常只需要一個門面類,并且此門面類只有一個實例鸿市,換言之它是一個單例類锯梁。當然這并不意味著在整個系統(tǒng)里只有一個門面類,而僅僅是說對每一個子系統(tǒng)只有一個門面類焰情∧暗剩或者說,如果一個系統(tǒng)有好幾個子系統(tǒng)的話内舟,每一個子系統(tǒng)都有一個門面類合敦,整個系統(tǒng)可以有數(shù)個門面類。

為子系統(tǒng)增加新行為

初學者往往以為通過繼承一個門面類便可在子系統(tǒng)中加入新的行為验游,這是錯誤的充岛。門面模式的用意是為子系統(tǒng)提供一個集中化和簡化的溝通管道,而不能向子系統(tǒng)加入新的行為耕蝉。比如醫(yī)院中的接待員并不是醫(yī)護人員崔梗,接待員并不能為病人提供醫(yī)療服務。

門面模式的優(yōu)點

門面模式的優(yōu)點:

●  松散耦合

門面模式松散了客戶端與子系統(tǒng)的耦合關系垒在,讓子系統(tǒng)內(nèi)部的模塊能更容易擴展和維護蒜魄。

●  簡單易用

門面模式讓子系統(tǒng)更加易用,客戶端不再需要了解子系統(tǒng)內(nèi)部的實現(xiàn)场躯,也不需要跟眾多子系統(tǒng)內(nèi)部的模塊進行交互谈为,只需要跟門面類交互就可以了。

●  更好的劃分訪問層次

通過合理使用Facade踢关,可以幫助我們更好地劃分訪問的層次伞鲫。有些方法是對系統(tǒng)外的,有些方法是系統(tǒng)內(nèi)部使用的耘成。把需要暴露給外部的功能集中到門面中榔昔,這樣既方便客戶端使用驹闰,也很好地隱藏了內(nèi)部的細節(jié)瘪菌。


門面模式在Tomcat中的使用

Tomcat中門面模式使用的很多,因為Tomcat中有很多不同組件嘹朗,每個組件要相互通信师妙,但是又不能將自己內(nèi)部數(shù)據(jù)過多的暴露給其他組件。用門面模式隔離數(shù)據(jù)是個很好的方法屹培。

下面是Request上使用的門面模式:

image.png

使用過Servlet的人都清楚默穴,除了要在web.xml做相應的配置外怔檩,還需繼承一個叫HttpServlet的抽象類,并且重寫doGet與doPost方法(當然只重寫service方法也是可以的)蓄诽。

public class TestServlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        
        this.doPost(request, response);
            
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
            
        
    }

}

可以看出doGet與doPost方法有兩個參數(shù)薛训,參數(shù)類型是接口HttpServletRequest與接口HttpServletResponse,那么從Tomcat中傳遞過來的真實類型到底是什么呢仑氛?通過debug會發(fā)現(xiàn)乙埃,在真正調(diào)用TestServlet類之前,會經(jīng)過很多Tomcat中的方法锯岖。如下圖所示

image.png

注意紅色方框圈中的類介袜,StandardWrapperValue類中的invoke方法225行代碼如下:

 filterChain.doFilter (request.getRequest(), response.getResponse());

在StandardWrapperValue類中并沒有直接將Request對象與Response對象傳遞給ApplicationFilterChain類的doFilter方法,傳遞的是RequestFacade與ResponseFacade對象出吹,為什么這么說呢遇伞,看一下request.getRequest()與response.getResponse()方法就真相大白了。

Request類

public HttpServletRequest getRequest() {
        if (facade == null) {
            facade = new RequestFacade(this);
        }
        return facade;
    }

Response類

public HttpServletResponse getResponse() {
        if (facade == null) {
            facade = new ResponseFacade(this);
        }
        return (facade);
    }

可以看到它們返回都是各自的一個門面類捶牢,那么這樣做有什么好處呢鸠珠?

Request對象中的很多方法都是內(nèi)部組件之間相互交互時使用的,比如setComet秋麸、setRequestedSessionId等方法(這里就不一一列舉了)跳芳。這些方法并不對外部公開,但是又必須設置為public竹勉,因為還需要跟內(nèi)部組件之間交互使用飞盆。最好的解決方法就是通過使用一個Facade類,將與內(nèi)部組件之間交互使用的方法屏蔽掉次乓,只提供給外部程序感興趣的方法吓歇。

如果不使用Facade類,直接傳遞的是Request對象和Response對象票腰,那么熟悉容器內(nèi)部運作的程序員可以分別把ServletRequest和ServletResponse對象向下轉換為Request和Response城看,并調(diào)用它們的公共方法。比如擁有Request對象杏慰,就可以調(diào)用setComet测柠、setRequestedSessionId等方法,這會危害安全性缘滥。

轉自:http://www.cnblogs.com/java-my-life/archive/2012/05/02/2478101.html

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末轰胁,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子朝扼,更是在濱河造成了極大的恐慌赃阀,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件擎颖,死亡現(xiàn)場離奇詭異榛斯,居然都是意外死亡观游,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進店門驮俗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來懂缕,“玉大人,你說我怎么就攤上這事王凑√嵊叮” “怎么了?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵荤崇,是天一觀的道長拌屏。 經(jīng)常有香客問我,道長术荤,這世上最難降的妖魔是什么倚喂? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮瓣戚,結果婚禮上端圈,老公的妹妹穿的比我還像新娘。我一直安慰自己子库,他們只是感情好舱权,可當我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著仑嗅,像睡著了一般宴倍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上仓技,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天鸵贬,我揣著相機與錄音,去河邊找鬼脖捻。 笑死阔逼,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的地沮。 我是一名探鬼主播嗜浮,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼摩疑!你這毒婦竟也來了危融?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤未荒,失蹤者是張志新(化名)和其女友劉穎专挪,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體片排,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡寨腔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了率寡。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片迫卢。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖冶共,靈堂內(nèi)的尸體忽然破棺而出乾蛤,到底是詐尸還是另有隱情,我是刑警寧澤捅僵,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布家卖,位于F島的核電站,受9級特大地震影響庙楚,放射性物質(zhì)發(fā)生泄漏上荡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一馒闷、第九天 我趴在偏房一處隱蔽的房頂上張望酪捡。 院中可真熱鬧,春花似錦纳账、人聲如沸逛薇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽永罚。三九已至,卻和暖如春卧秘,著一層夾襖步出監(jiān)牢的瞬間尤蛮,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工斯议, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留产捞,地道東北人。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓哼御,卻偏偏與公主長得像坯临,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子恋昼,可洞房花燭夜當晚...
    茶點故事閱讀 44,864評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 1 場景問題# 1.1 生活中的示例## 外觀模式在現(xiàn)實生活中的示例很多看靠,比如:組裝電腦,通常會有兩種方案液肌。 一個...
    七寸知架構閱讀 6,239評論 7 57
  • 定義 門面模式是對象的結構模式挟炬,外部與一個子系統(tǒng)的通信必須通過一個統(tǒng)一的門面對象進行。門面模式提供一個高層次的接口...
    步積閱讀 2,212評論 0 3
  • 本篇文章介紹一種設計模式——外觀模式。本篇文章內(nèi)容參考《JAVA與模式》之門面模式谤祖,外觀模式婿滓,深入淺出外觀模式(二...
    Ruheng閱讀 7,055評論 0 8
  • 第四篇 如何贏得他人的贊同 25. 如果錯了,當即承認 人貴五品:認錯粥喜,柔和凸主,生忍,溝通额湘,放下卿吐。但是人們常常不肯認...
    禪園聽雪閱讀 1,263評論 1 13
  • 在閑暇時 會想起一些歡笑,一些美好 一個美麗的你 或者锋华,一段熟悉的音樂 一本書嗡官,一句話 一個似曾相識的街景 也會走...
    第四劍閱讀 248評論 0 0