java設(shè)計(jì)模式-門(mén)面模式(外觀模式 Facade)

定義

門(mén)面模式是對(duì)象的結(jié)構(gòu)模式九昧,外部與一個(gè)子系統(tǒng)的通信必須通過(guò)一個(gè)統(tǒng)一的門(mén)面對(duì)象進(jìn)行扰肌。門(mén)面模式提供一個(gè)高層次的接口能曾,使得子系統(tǒng)更易于使用。

醫(yī)院的例子

現(xiàn)在的軟件系統(tǒng)都是比較復(fù)雜的项滑,設(shè)計(jì)師處理復(fù)雜問(wèn)題的一個(gè)常見(jiàn)方法就是將其“分而治之”上炎,把一個(gè)系統(tǒng)拆分成幾個(gè)較小的子系統(tǒng)谨读。如果把一個(gè)醫(yī)院作為一個(gè)軟件系統(tǒng),按照部門(mén)職能晤锹,這個(gè)系統(tǒng)可以劃分為掛號(hào)摩幔、門(mén)診、劃價(jià)鞭铆、化驗(yàn)或衡、收費(fèi)、取藥等多個(gè)子系統(tǒng)车遂》舛希看病的病人需要同這些部門(mén)打交道,就如同一個(gè)系統(tǒng)的客戶(hù)端與系統(tǒng)的各個(gè)類(lèi)打交道一樣舶担,不是一件容易的事情坡疼。

首先病人必須先掛號(hào),然后門(mén)診衣陶。如果醫(yī)生要求化驗(yàn)柄瑰,則病人需要先劃價(jià),然后繳費(fèi)剪况,才能到化驗(yàn)部門(mén)進(jìn)行化驗(yàn)檢查教沾。拿到化驗(yàn)結(jié)果后,再回到門(mén)診室拯欧。

醫(yī)院就診流程

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

解決這種不變的方法就是引進(jìn)門(mén)面模式镐作,醫(yī)院可以設(shè)置一個(gè)接待員的位置藏姐,由接待員負(fù)責(zé)代為掛號(hào)、劃價(jià)该贾、繳費(fèi)羔杨、取藥等過(guò)程。這個(gè)接待員就是門(mén)面模式的體現(xiàn)杨蛋,病人只需要接觸接待員兜材,由接待員與各個(gè)部門(mén)打交道。

醫(yī)院門(mén)面模式體現(xiàn)

門(mén)面模式的結(jié)構(gòu)

門(mén)面模式?jīng)]有一個(gè)一般化的類(lèi)圖描述逞力,最好的描述方法實(shí)際上就是以一個(gè)例子說(shuō)明曙寡。

門(mén)面模式結(jié)構(gòu)圖

由于門(mén)面模式的結(jié)構(gòu)圖過(guò)于抽象,因此把它稍微具體一點(diǎn)寇荧。假設(shè)系統(tǒng)有三個(gè)模塊举庶,分別是ModuleAModuleBModuleC揩抡,他們分別有一個(gè)示例方法户侥,那么此時(shí)示例的整體結(jié)構(gòu)圖如下:

門(mén)面模式具體設(shè)計(jì)圖示例

在這個(gè)對(duì)象圖中,出現(xiàn)了兩個(gè)角色:

  • 門(mén)面角色(Facade):客戶(hù)端可以調(diào)用這個(gè)角色的方法峦嗤。此角色知曉相關(guān)的(一個(gè)或多個(gè))子系統(tǒng)的功能和責(zé)任蕊唐。在正常情況下,本角色會(huì)將所有從客戶(hù)端發(fā)來(lái)的請(qǐng)求委派到相應(yīng)的子系統(tǒng)去烁设。
  • 子系統(tǒng)角色(SubSystem):可以同時(shí)有一個(gè)或多個(gè)子系統(tǒng)替梨,每個(gè)子系統(tǒng)都不是一個(gè)單獨(dú)的類(lèi),而是一個(gè)類(lèi)的集合(例如上面的子系統(tǒng)由三個(gè)ModuleA装黑、ModuleBModuleC三個(gè)類(lèi)組成)耙替。每個(gè)子系統(tǒng)都可以被客戶(hù)端直接調(diào)用,或者被門(mén)面角色調(diào)用曹体。子系統(tǒng)并不知道門(mén)面的存在俗扇,對(duì)于子系統(tǒng)而言,門(mén)面僅僅是另外一個(gè)客戶(hù)端而已箕别。

示例代碼

子系統(tǒng)角色中的類(lèi)

 public class ModuleA {
    public void testA() {
        System.out.println("調(diào)用了ModuleA中的testA方法铜幽。");
    }
}
 public class ModuleB {
    public void testB() {
        System.out.println("調(diào)用了ModuleB中的testB方法。");
    }
}
 public class ModuleC {
    public void testC() {
        System.out.println("調(diào)用了ModuleC中的testC方法串稀。");
    }
}

門(mén)面角色類(lèi):

public class Facade {
    public void test() {
        ModuleA moduleA = new ModuleA();
        moduleA.testA();
        ModuleB moduleB = new ModuleB();
        moduleB.testB();
        ModuleC moduleC = new ModuleC();
        moduleC.testC();
    }
}

客戶(hù)端角色類(lèi)

public class Client {
    public static void main(String[] args) {
        Facade facade = new Facade();
        facade.test();
    }
}

Facade類(lèi)其實(shí)相當(dāng)于A除抛、B、C模塊的外觀界面母截,有了這個(gè)Facade類(lèi)到忽,那么客戶(hù)端就不需要親自調(diào)用子系統(tǒng)中的A、B、C模塊了喘漏,也不需要知道系統(tǒng)內(nèi)部的實(shí)現(xiàn)細(xì)節(jié)护蝶,甚至都不需要知道A、B翩迈、C模塊的存在持灰,客戶(hù)端只需要跟Facade類(lèi)交互就好了,從而更好的實(shí)現(xiàn)了客戶(hù)端和子系統(tǒng)中A负饲、B堤魁、C模塊的解耦,讓客戶(hù)端更容易的使用系統(tǒng)返十。


門(mén)面模式的實(shí)現(xiàn)

使用門(mén)面模式還有一個(gè)附帶的好處妥泉,就是能夠有選擇性的暴露方法。一個(gè)模塊中定義的方法可以分成兩部分:一部分是給子系統(tǒng)外部使用的洞坑;一部分是子系統(tǒng)內(nèi)部模塊之間相互調(diào)用時(shí)使用的盲链。有了Facade類(lèi),那么用于子系統(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)用時(shí)使用的方法
    */
    public void a2() {}
    public void a3() {}
}
public class ModuleB {
    /**
    * 提供給子系統(tǒng)外部使用的方法
    */
    public void b1() {}
    
    /**
    * 子系統(tǒng)內(nèi)部模塊之間相互調(diào)用時(shí)使用的方法
    */
    public void b2() {}
    public void b3() {}
}
public class ModuleC {
    /**
    * 提供給子系統(tǒng)外部使用的方法
    */
    public void c1() {}
    
    /**
    * 子系統(tǒng)內(nèi)部模塊之間相互調(diào)用時(shí)使用的方法
    */
    public void c2() {}
    public void c3() {}
}

模塊門(mén)面裝飾類(lèi)

public class ModuleFacade() {
    ModuleA a = new ModuleA();
    ModuleB b = new ModuleB();
    ModuleC c = new ModuleC();
    
    /**
    * 下面這些是A、B攻泼、C模塊對(duì)子系統(tǒng)外部提供的方法
    */
    public void a1() {
        a.a1();
    }
    public void b1() {
        b.b1();
    }
    public void c1() {
        c.c1();
    }
}

這樣定義一個(gè)ModuleFacade類(lèi)可以有效的屏蔽內(nèi)部的細(xì)節(jié)火架,免得客戶(hù)端去調(diào)用Module的相關(guān)類(lèi)時(shí),發(fā)現(xiàn)一些不需要它知道的方法忙菠,比如a2()a3()方法就不需要讓客戶(hù)端知道何鸡,否則既暴露了內(nèi)部的細(xì)節(jié),又讓客戶(hù)端迷惑牛欢。對(duì)客戶(hù)端來(lái)說(shuō)骡男,他可能還需要去考慮a2()a3()方法的作用。其實(shí)a2()a3()方法是內(nèi)部模塊之間交互的傍睹,原本就不是對(duì)子系統(tǒng)外部的隔盛,所以干脆就不要讓客戶(hù)端知道。

一個(gè)系統(tǒng)可以有幾個(gè)門(mén)面類(lèi)

在門(mén)面模式中拾稳,通常只需要一個(gè)門(mén)面類(lèi)吮炕,并且此門(mén)面類(lèi)只有一個(gè)實(shí)例,換句話說(shuō)访得,它是一個(gè)單例類(lèi)龙亲。當(dāng)然這并不意味著在整個(gè)系統(tǒng)中只能有一個(gè)門(mén)面類(lèi),而僅僅是說(shuō)對(duì)于一個(gè)子系統(tǒng)只有一個(gè)門(mén)面類(lèi)■或者說(shuō)杜耙,如果一個(gè)系統(tǒng)有好幾個(gè)子系統(tǒng)的話,每個(gè)子系統(tǒng)都有一個(gè)門(mén)面類(lèi)迎膜,整個(gè)系統(tǒng)可以有數(shù)個(gè)門(mén)面類(lèi)泥技。

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

初學(xué)者往往認(rèn)為通過(guò)繼承一個(gè)門(mén)面類(lèi)便可以在子系統(tǒng)中加入新的行為浆兰,這是錯(cuò)誤的磕仅。門(mén)面模式的用意是為子系統(tǒng)提供一個(gè)集中化和簡(jiǎn)化的溝通管道,而不能向子系統(tǒng)中增加新的行為簸呈。比如醫(yī)院中的接待人員不是醫(yī)護(hù)人員榕订,接待員并不能為病人提供醫(yī)療服務(wù)。

門(mén)面模式的優(yōu)點(diǎn)

  • 松散耦合
    門(mén)面模式松散了客戶(hù)端與子系統(tǒng)的耦合關(guān)系蜕便,讓子系統(tǒng)內(nèi)部的模塊能更容易拓展和維護(hù)劫恒。
  • 簡(jiǎn)單易用
    門(mén)面模式讓子系統(tǒng)更加易用,客戶(hù)端不再需要了解子系統(tǒng)內(nèi)部的實(shí)現(xiàn)轿腺,也不需要跟眾多子系統(tǒng)內(nèi)部的模塊進(jìn)行交互两嘴,只需要跟門(mén)面類(lèi)交互即可。
  • 更好的劃分訪問(wèn)層次
    通過(guò)合理的使用門(mén)面模式Facade族壳,可以幫助我們更好的劃分訪問(wèn)的層次憔辫。有些方法是對(duì)系統(tǒng)外部的,有些方法是對(duì)系統(tǒng)內(nèi)部使用的仿荆。把需要暴露給外部的功能集中到門(mén)面類(lèi)中贰您,這樣既方便客戶(hù)端使用,也很好的隱藏了內(nèi)部的細(xì)節(jié)拢操。

門(mén)面模式在Tomcat中的使用

Tomcat中門(mén)面模式使用的很多锦亦,因?yàn)門(mén)omcat中有很多不同組件,每個(gè)組件要相互通信令境,但是又不能將自己的內(nèi)部數(shù)據(jù)過(guò)多的暴露給其他組件杠园。用門(mén)面模式隔離數(shù)據(jù)是很好的方法。

下面是Request上使用的門(mén)面模式:

Request上使用的門(mén)面模式

使用過(guò)Servlet的人都清楚舔庶,除了要在web.xml做相應(yīng)的配置外抛蚁,還需要繼承一個(gè)叫HttpServlet的抽象類(lèi),并且重寫(xiě)doGetdoPost方法(當(dāng)然只重寫(xiě)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 {
    }
}

可以看出doGetdoPost方法又兩個(gè)參數(shù)篮绿,參數(shù)類(lèi)型是接口HttpServletRequest與接口HttpServletResponse,那么從Tomcat中傳遞國(guó)來(lái)的真實(shí)類(lèi)型到底是什么呢吕漂?通過(guò)Debug會(huì)發(fā)現(xiàn)亲配,在真正調(diào)用TestServlet之前,會(huì)經(jīng)歷很多Tomcat中的方法。如下圖所示:

Tomcat調(diào)試過(guò)程

注意紅色方框圈中的類(lèi)吼虎,StandardWrapperValue類(lèi)中的invoke方法225行代碼如下:

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

StandardWrapperValue類(lèi)并沒(méi)有直接將Request對(duì)象與Response對(duì)象傳遞給ApplicationFilterChain類(lèi)的doFilter()方法犬钢,傳遞的是RequestFacadeResponseFacade對(duì)象,為什么這么說(shuō)呢思灰,看一下request.getRequest()response.getResponse()方法就真相大白了玷犹。

Request類(lèi)

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

Response類(lèi)

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

可以看到它們返回的都是各自的一個(gè)門(mén)面類(lèi),那么這樣做有什么好處呢洒疚?

Request對(duì)象中的很多方法都是內(nèi)部組件之間相互交互時(shí)使用的歹颓,例如setCometsetRequestedSessionId等方法(這里不一一列舉)油湖。這些方法并不對(duì)外部公開(kāi)巍扛,但是又必須設(shè)置為public因?yàn)檫€需要跟內(nèi)部組件之間交互使用。最好的解決方法就是通過(guò)使用一個(gè)Facade類(lèi)乏德,將與內(nèi)部組件之間交互使用的方法屏蔽掉撤奸,只提供給外部程序感興趣的方法。

如果不使用Facade類(lèi)喊括,直接傳遞的是Request對(duì)象與Response對(duì)象胧瓜,那么熟悉容器內(nèi)部運(yùn)作的程序員可以分別把ServletRequestServletResponse對(duì)象向下轉(zhuǎn)換為RequestResponse,并調(diào)用它們的公共方法郑什,比如擁有Request對(duì)象府喳,就可以調(diào)用setCometsetRequestedSessionId等方法蹦误,這樣會(huì)危害安全性劫拢。

參考

《JAVA與模式》之門(mén)面模式

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市强胰,隨后出現(xiàn)的幾起案子舱沧,更是在濱河造成了極大的恐慌,老刑警劉巖偶洋,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件熟吏,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡玄窝,警方通過(guò)查閱死者的電腦和手機(jī)牵寺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)恩脂,“玉大人帽氓,你說(shuō)我怎么就攤上這事×┛椋” “怎么了黎休?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵浓领,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我势腮,道長(zhǎng)联贩,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任捎拯,我火速辦了婚禮泪幌,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘署照。我一直安慰自己祸泪,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布藤树。 她就那樣靜靜地躺著浴滴,像睡著了一般拓萌。 火紅的嫁衣襯著肌膚如雪岁钓。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,394評(píng)論 1 310
  • 那天微王,我揣著相機(jī)與錄音屡限,去河邊找鬼。 笑死炕倘,一個(gè)胖子當(dāng)著我的面吹牛钧大,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播罩旋,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼啊央,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了涨醋?” 一聲冷哼從身側(cè)響起瓜饥,我...
    開(kāi)封第一講書(shū)人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎浴骂,沒(méi)想到半個(gè)月后乓土,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡溯警,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年趣苏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片梯轻。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡食磕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出喳挑,到底是詐尸還是另有隱情彬伦,我是刑警寧澤萄金,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站媚朦,受9級(jí)特大地震影響氧敢,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜询张,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一孙乖、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧份氧,春花似錦唯袄、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至厅缺,卻和暖如春蔬顾,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背湘捎。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工诀豁, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人窥妇。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓舷胜,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親活翩。 傳聞我的和親對(duì)象是個(gè)殘疾皇子烹骨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359

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

  • 1 場(chǎng)景問(wèn)題# 1.1 生活中的示例## 外觀模式在現(xiàn)實(shí)生活中的示例很多,比如:組裝電腦材泄,通常會(huì)有兩種方案沮焕。 一個(gè)...
    七寸知架構(gòu)閱讀 6,251評(píng)論 7 57
  • 本篇文章介紹一種設(shè)計(jì)模式——外觀模式。本篇文章內(nèi)容參考《JAVA與模式》之門(mén)面模式脸爱,外觀模式遇汞,深入淺出外觀模式(二...
    Ruheng閱讀 7,074評(píng)論 0 8
  • 設(shè)計(jì)模式匯總 一、基礎(chǔ)知識(shí) 1. 設(shè)計(jì)模式概述 定義:設(shè)計(jì)模式(Design Pattern)是一套被反復(fù)使用簿废、多...
    MinoyJet閱讀 3,949評(píng)論 1 15
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法空入,類(lèi)相關(guān)的語(yǔ)法,內(nèi)部類(lèi)的語(yǔ)法族檬,繼承相關(guān)的語(yǔ)法歪赢,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚(yú)_t_閱讀 31,664評(píng)論 18 399
  • 只思考本質(zhì)的思考方法: 最近睡眠不好单料,沒(méi)有鍛煉埋凯,導(dǎo)致精神狀態(tài)極其差点楼,便導(dǎo)致人生這個(gè)時(shí)間段的質(zhì)量非常低。當(dāng)然白对,也體驗(yàn)...
    神樂(lè)醬醬醬閱讀 142評(píng)論 0 0