設(shè)計(jì)模式之——門面模式

1 門面模式的定義

門面模式:Facade Pattern坡贺,也叫做外觀模式。要求一個(gè)子系統(tǒng)的外部與其內(nèi)部的通信必須通過一個(gè)統(tǒng)一的對象進(jìn)行。門面模式提供一個(gè)高層次的接口,使得子系統(tǒng)更易于使用患膛。
門面模式是一種比較常用的封裝模式,注重“統(tǒng)一的對象”耻蛇,也就是提供一個(gè)訪問子系統(tǒng)的接口踪蹬,除了這個(gè)接口不允許有任何訪問子系統(tǒng)的行為發(fā)生驹溃。
門面模式的通用類圖:

門面模式通用類圖

門面對象,是外界訪問子系統(tǒng)內(nèi)部的唯一途徑延曙,不管子系統(tǒng)內(nèi)部多么雜亂無章,只要有門面對象亡哄,就可以簡單訪問子系統(tǒng)枝缔。

  • Facade門面角色
    客戶端可以調(diào)用門面角色的方法,門面角色知曉子系統(tǒng)的所有功能和職責(zé)蚊惯。一般情況下愿卸,本角色會(huì)將所有從客戶端發(fā)來的請求委派到相應(yīng)的子系統(tǒng)去,也就是說門面角色沒有實(shí)際的業(yè)務(wù)邏輯截型,只是一個(gè)委托類趴荸。
  • Subsystem子系統(tǒng)角色
    可以同時(shí)有一個(gè)或者多個(gè)子系統(tǒng),每一個(gè)子系統(tǒng)都不是一個(gè)單獨(dú)的類宦焦,而是一個(gè)類的集合发钝。子系統(tǒng)并不知道門面的存在,對于子系統(tǒng)而言波闹,門面僅僅是另外一個(gè)客戶端而已酝豪。

2 門面模式通用示例模式

2.1 子系統(tǒng)通用代碼

子系統(tǒng)是類的集合,并且每一個(gè)子系統(tǒng)都不相同精堕,我們使用3個(gè)相互無關(guān)的類來代表孵淘。可以認(rèn)為這3個(gè)類屬于近鄰歹篓,處理相關(guān)的業(yè)務(wù)瘫证,因此可以認(rèn)為是一個(gè)子系統(tǒng)的不同邏輯處理模塊,對于此子系統(tǒng)的訪問需要通過門面進(jìn)行庄撮。

  1. 業(yè)務(wù)類A
@Slf4j
public class SubsystemA {

    /**
     * 業(yè)務(wù)邏輯A
     */
    public void doSomethingA() {
        log.info("{}的業(yè)務(wù)邏輯背捌。", this.getClass().getSimpleName());
    }
}
  1. 業(yè)務(wù)類B
@Slf4j
public class SubsystemB {

    /**
     * 業(yè)務(wù)邏輯B
     */
    public void doSomethingB() {
        log.info("{}的業(yè)務(wù)邏輯。", this.getClass().getSimpleName());
    }
}
  1. 業(yè)務(wù)類C
@Slf4j
public class SubsystemC {

    /**
     * 業(yè)務(wù)邏輯C
     */
    public void doSomethingC() {
        log.info("{}的業(yè)務(wù)邏輯洞斯。", this.getClass().getSimpleName());
    }
}

2.2 門面對象

@Slf4j
public class Facade {
    //被委托的對象
    private SubsystemA subsystemA = new SubsystemA();
    private SubsystemB subsystemB = new SubsystemB();
    private SubsystemC subsystemC = new SubsystemC();

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

    public void businessB() {
        this.subsystemB.doSomethingB();
    }

    public void businessC() {
        this.subsystemC.doSomethingC();
    }
}

2.3 場景類

@Slf4j
public class Client {
    public static void main(String[] args) {
        Facade facade = new Facade();
        facade.businessA();
        facade.businessB();
        facade.businessC();
    }
}

運(yùn)行結(jié)果:

21:20:14.042 [main] INFO com.idear.design.pattern.facade.SubsystemA - SubsystemA的業(yè)務(wù)邏輯载萌。
21:20:14.046 [main] INFO com.idear.design.pattern.facade.SubsystemB - SubsystemB的業(yè)務(wù)邏輯。
21:20:14.046 [main] INFO com.idear.design.pattern.facade.SubsystemC - SubsystemC的業(yè)務(wù)邏輯巡扇。

3 門面模式的優(yōu)缺點(diǎn)

3.1 優(yōu)點(diǎn)

  1. 減少系統(tǒng)的相互依賴
    門面模式可以讓場景類只需要依賴門面對象扭仁,而與子系統(tǒng)無關(guān)。因此可以降低系統(tǒng)耦合厅翔。
  2. 提高靈活性
    以來減少了乖坠,不管子系統(tǒng)內(nèi)部如何變化,只要不修改門面對象的對外接口就行刀闷,提高了靈活性熊泵。
  3. 提高安全性
    外部只能通過門面訪問子系統(tǒng)的功能仰迁,門面沒有開放的就不能訪問,提高了子系統(tǒng)的安全性顽分。

3.2 缺點(diǎn)

門面模式最大的缺點(diǎn)是不符合開閉原則徐许。系統(tǒng)投產(chǎn)后,一旦發(fā)現(xiàn)錯(cuò)誤卒蘸,九比西藥修改門面角色的代碼雌隅,風(fēng)險(xiǎn)比較大。

4 門面模式的使用場景

4.1 使用場景

  1. 當(dāng)一個(gè)復(fù)雜的系統(tǒng)模塊或者子系統(tǒng)需要向外界提供一個(gè)訪問接口的時(shí)候缸沃;
  2. 子系統(tǒng)相對獨(dú)立——其他(子)系統(tǒng)對該系統(tǒng)的訪問只需要黑箱操作恰起,不需要關(guān)注內(nèi)部實(shí)現(xiàn)細(xì)節(jié);
  3. 預(yù)防低水平人員帶來的風(fēng)險(xiǎn)擴(kuò)散
    為降低個(gè)人代碼質(zhì)量對整體項(xiàng)目的影響風(fēng)險(xiǎn)趾牧,一般指定相關(guān)人員在特定的子系統(tǒng)中進(jìn)行開發(fā)检盼,然后提供門面接口進(jìn)行訪問操作。

4.2 注意事項(xiàng)

  1. 一個(gè)子系統(tǒng)可以有多個(gè)門面
    一般情況下翘单,一個(gè)子系統(tǒng)只要有一個(gè)門面就夠了吨枉。但是,當(dāng)以下情況可以有多個(gè):
  • 門面已經(jīng)過于龐大繁雜
    代碼行數(shù)太多哄芜,包含業(yè)務(wù)邏輯太多东羹。此時(shí)可以按照職責(zé)拆分為多個(gè)門面。例如忠烛,用戶信息的更新属提、創(chuàng)建、查詢分別提供門面美尸。
  • 子系統(tǒng)可以提供不同的訪問路徑
    比如示例代碼中冤议,可能業(yè)務(wù)模塊A需要Facade的所有模塊,但是業(yè)務(wù)模塊B只需要Facade的businessA()業(yè)務(wù)师坎。此時(shí)A就可以使用Facade恕酸,而單獨(dú)為業(yè)務(wù)模塊B提供一個(gè)只包含businessA()業(yè)務(wù)的Facade,提高安全性胯陋。
  1. 門面不參與子系統(tǒng)內(nèi)部的業(yè)務(wù)邏輯
    以2中的代碼為例蕊温,如果業(yè)務(wù)businessC()必須先調(diào)用SubsystemA.doSomethingA(),然后調(diào)用SubsystemC.doSomethingC()遏乔。這時(shí)候义矛,可能很多時(shí)候都會(huì)按照如下進(jìn)行設(shè)計(jì):
    錯(cuò)誤示例
    public void businessC() {
        this.subsystemA.doSomethingA();
        this.subsystemC.doSomethingC();
    }

這種設(shè)計(jì)很不靠譜,因?yàn)殚T面對象參與了邏輯盟萨。門面對象應(yīng)該只是提供訪問子系統(tǒng)的路徑凉翻,不應(yīng)該也不能參與具體的業(yè)務(wù)邏輯,否則就會(huì)產(chǎn)生依賴倒置的問題:子系統(tǒng)必須依賴門面才能被訪問捻激,同時(shí)違反了單一職責(zé)原則制轰,破壞了系統(tǒng)的封裝性前计。
那么如何解決呢?
建立一個(gè)封裝類垃杖,封裝完畢后提供給門面對象使用男杈。Context封裝類提供了一個(gè)聯(lián)合業(yè)務(wù)joinBusinessC(),并且運(yùn)行在子系統(tǒng)內(nèi)部调俘。Context向門面對象提供joinBusinessC()業(yè)務(wù)邏輯伶棒。
封裝對象Context

@Slf4j
public class Context {
    //被委托的對象
    private SubsystemA subsystemA = new SubsystemA();
    private SubsystemC subsystemC = new SubsystemC();

    /**
     * 聯(lián)合功能
     */
    public void joinBusinessC() {
        this.subsystemA.doSomethingA();
        this.subsystemC.doSomethingC();
    }
}

門面對象

@Slf4j
public class FacadeJoin {
    //被委托的對象
    private SubsystemA subsystemA = new SubsystemA();
    private SubsystemB subsystemB = new SubsystemB();
    private Context context = new Context();

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

    public void businessB() {
        this.subsystemB.doSomethingB();
    }

    public void businessC() {
        this.context.joinBusinessC();
    }
}

通過封裝,業(yè)務(wù)只需要關(guān)注門面對象脉漏,具體的業(yè)務(wù)邏輯封裝在子系統(tǒng)內(nèi)部。即便有一天業(yè)務(wù)發(fā)生了變化袖牙,變化也會(huì)被封裝在子系統(tǒng)內(nèi)部侧巨,對于外部調(diào)用者來說,還是同一個(gè)門面鞭达,同樣的方法司忱,符合開閉原則和依賴倒置原則。

參考

  1. 設(shè)計(jì)模式之禪
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末畴蹭,一起剝皮案震驚了整個(gè)濱河市坦仍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌叨襟,老刑警劉巖繁扎,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異糊闽,居然都是意外死亡梳玫,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進(jìn)店門右犹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來提澎,“玉大人,你說我怎么就攤上這事念链∨渭桑” “怎么了?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵掂墓,是天一觀的道長谦纱。 經(jīng)常有香客問我,道長君编,這世上最難降的妖魔是什么服协? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮啦粹,結(jié)果婚禮上偿荷,老公的妹妹穿的比我還像新娘窘游。我一直安慰自己,他們只是感情好跳纳,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布忍饰。 她就那樣靜靜地躺著,像睡著了一般寺庄。 火紅的嫁衣襯著肌膚如雪艾蓝。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天斗塘,我揣著相機(jī)與錄音赢织,去河邊找鬼。 笑死馍盟,一個(gè)胖子當(dāng)著我的面吹牛于置,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播贞岭,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼八毯,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了瞄桨?” 一聲冷哼從身側(cè)響起话速,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎芯侥,沒想到半個(gè)月后泊交,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡柱查,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年活合,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片物赶。...
    茶點(diǎn)故事閱讀 39,926評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡白指,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出酵紫,到底是詐尸還是另有隱情告嘲,我是刑警寧澤,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布奖地,位于F島的核電站橄唬,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏参歹。R本人自食惡果不足惜仰楚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧僧界,春花似錦侨嘀、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至葬荷,卻和暖如春涨共,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背宠漩。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工举反, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人扒吁。 一個(gè)月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓火鼻,卻偏偏與公主長得像,于是被迫代替她去往敵國和親瘦陈。 傳聞我的和親對象是個(gè)殘疾皇子凝危,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評論 2 354

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