每天學習一個設計模式(五):結構型之門面模式

来源:https://www.cnblogs.com/aohongzhu/archive/2020/05/22/12938733.html
-Advertisement-
Play Games

一、基本概念 門面模式(外觀模式)是對象的結構模式,外部與一個子系統的通信必須通過一個統一的門面對象進行。門面模式提供一個高層次的介面,使得子系統更易於使用。 二、通俗解釋 FACADE門面模式:我有一個專業的Nikon相機,我就喜歡自己手動調光圈、快門,這樣照出來的照片才專業,但MM可不懂這些,教 ...


一、基本概念

門面模式(外觀模式)是對象的結構模式,外部與一個子系統的通信必須通過一個統一的門面對象進行。門面模式提供一個高層次的介面,使得子系統更易於使用。

二、通俗解釋

FACADE門面模式:我有一個專業的Nikon相機,我就喜歡自己手動調光圈、快門,這樣照出來的照片才專業,但MM可不懂這些,教了半天也不會。幸好相機有Facade設計模式,把相機調整到自動檔,只要對準目標按快門就行了,一切由相機自動調整,這樣MM也可以用這個相機給我拍張照片了。 門面模式:外部與一個子系統的通信必須通過一個統一的門面對象進行。門面模式提供一個高層次的介面,使得子系統更易於使用。每一個子系統只有一個門面類,而且此門面類只有一個實例,也就是說它是一個單例模式。但整個系統可以有多個門面類。

醫院的例子

現代的軟體系統都是比較複雜的,設計師處理複雜系統的一個常見方法便是將其“分而治之”,把一個系統劃分為幾個較小的子系統。如果把醫院作為一個子系統,按照部門職能,這個系統可以劃分為掛號、門診、劃價、化驗、收費、取藥等。看病的病人要與這些部門打交道,就如同一個子系統的客戶端與一個子系統的各個類打交道一樣,不是一件容易的事情。

首先病人必須先掛號,然後門診。如果醫生要求化驗,病人必須首先劃價,然後繳費,才可以到化驗部門做化驗。化驗後再回到門診室。

                                             

上圖描述的是病人在醫院里的體驗,圖中的方框代表醫院。

解決這種不便的方法便是引進門面模式,醫院可以設置一個接待員的位置,由接待員負責代為掛號、劃價、繳費、取藥等。這個接待員就是門面模式的體現,病人只接觸接待員,由接待員與各個部門打交道。

                                          

三、門面模式詳解

1.門面模式的結構

門面模式沒有一個一般化的類圖描述,最好的描述方法實際上就是以一個例子說明。

                                         

由於門面模式的結構圖過於抽象,因此把它稍稍具體點。假設子系統內有三個模塊,分別是ModuleA、ModuleB和ModuleC,它們分別有一個示例方法,那麼此時示例的整體結構圖如下:

                                      

在這個對象圖中,出現了兩個角色:

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

 ● 子系統(SubSystem)角色 :可以同時有一個或者多個子系統。每個子系統都不是一個單獨的類,而是一個類的集合(如上面的子系統就是由ModuleA、ModuleB、ModuleC三個類組合而成)。每個子系統都可以被客戶端直接調用,或者被門面角色調用。子系統並不知道門面的存在,對於子系統而言,門面僅僅是另外一個客戶端而已。

子系統角色中的類:

public class ModuleA {
    //示意方法
    public void testA(){
        System.out.println("調用ModuleA中的testA方法");
    }
}
public class ModuleB {
    //示意方法
    public void testB(){
        System.out.println("調用ModuleB中的testB方法");
    }
}
public class ModuleC {
    //示意方法
    public void testC(){
        System.out.println("調用ModuleC中的testC方法");
    }
}

門面角色類:

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

客戶端角色類:

public class Client {

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

}

Facade類其實相當於A、B、C模塊的外觀界面,有了這個Facade類,那麼客戶端就不需要親自調用子系統中的A、B、C模塊了,也不需要知道系統內部的實現細節,甚至都不需要知道A、B、C模塊的存在,客戶端只需要跟Facade類交互就好了,從而更好地實現了客戶端和子系統中A、B、C模塊的解耦,讓客戶端更容易地使用系統。

2.門面模式的實現

使用門面模式還有一個附帶的好處,就是能夠有選擇性地暴露方法。一個模塊中定義的方法可以分成兩部分,一部分是給子系統外部使用的,一部分是子系統內部模塊之間相互調用時使用的。有了Facade類,那麼用於子系統內部模塊之間相互調用的方法就不用暴露給子系統外部了。

比如,定義如下A、B、C模塊。

public class ModuleA {
    /**
     * 提供給子系統外部使用的方法
     */
    public void a1(){};
    
    /**
     * 子系統內部模塊之間相互調用時使用的方法
     */
    public void a2(){};
    public void a3(){};
}
public class ModuleB {
    /**
     * 提供給子系統外部使用的方法
     */
    public void b1(){};
    
    /**
     * 子系統內部模塊之間相互調用時使用的方法
     */
    public void b2(){};
    public void b3(){};
}
public class ModuleC {
    /**
     * 提供給子系統外部使用的方法
     */
    public void c1(){};
    
    /**
     * 子系統內部模塊之間相互調用時使用的方法
     */
    public void c2(){};
    public void c3(){};
}

門面類:

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

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

一個系統可以有幾個門面類

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

為子系統增加新行為

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

3.門面模式的優點

門面模式的優點:

 ● 鬆散耦合

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

 ● 簡單易用

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

 ● 更好的劃分訪問層次

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

四、門面模式在Tomcat中的使用

Tomcat中門面模式使用的很多,因為Tomcat中有很多不同組件,每個組件要相互通信,但是又不能將自己內部數據過多的暴露給其他組件。用門面模式隔離數據是個很好的方法。

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

                                                                

使用過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方法有兩個參數,參數類型是介面HttpServletRequest與介面HttpServletResponse,那麼從Tomcat中傳遞過來的真實類型到底是什麼呢?通過debug會發現,在真正調用TestServlet類之前,會經過很多Tomcat中的方法。如下圖所示

                       

註意紅色方框圈中的類,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對象中的很多方法都是內部組件之間相互交互時使用的,比如setComet、setRequestedSessionId等方法(這裡就不一一列舉了)。這些方法並不對外部公開,但是又必須設置為public,因為還需要跟內部組件之間交互使用。最好的解決方法就是通過使用一個Facade類,將與內部組件之間交互使用的方法屏蔽掉,只提供給外部程式感興趣的方法。

如果不使用Facade類,直接傳遞的是Request對象和Response對象,那麼熟悉容器內部運作的程式員可以分別把ServletRequest和ServletResponse對象向下轉換為Request和Response,並調用它們的公共方法。比如擁有Request對象,就可以調用setComet、setRequestedSessionId等方法,這會危害安全性。


(引用:《JAVA與模式》之門面模式


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 隨著技術以如此快的速度發展,現在我們有必要選擇合適的工具來使用。每個軟體項目都有它需要滿足的多個需求和規範,因此為了滿足這些需求,選擇一種編程語言以允許您以有效的方式開發和管理項目非常重要。 由於有許多種編程語言和框架可供選擇,它們之間的比較已成為必然,因為你需要知道哪一個提供了最好的服務。當涉及到 ...
  • 什麼是NodeJS JS是腳本語言,腳本語言都需要一個解析器才能運行。對於寫在HTML頁面里的JS,瀏覽器充當瞭解析器的角色。而對於需要獨立運行的JS,NodeJS就是一個解析器。 每一種解析器都是一個運行環境,不但允許JS定義各種數據結構,進行各種計算,還允許JS使用運行環境提供的內置對象和方法做 ...
  • 之前一直採用VS進行各種前端後端的開發,隨著項目的需要,正逐步融合純前端的開發模式,開始主要選型為Vue + Element 進行BS前端的開發,後續會進一步整合Vue + AntDesign的界面套件,作為兩種不同界面框架的展現方式。採用Vue + Element 的前端開發和之前的開發模式需要有... ...
  • 從事web前端6年的工作,曾經是信息管理的一名應屆生,由於專業難找工作,掙錢少,當時選擇了轉行學前端開發技術,今天師兄就給大家講一下,作為應屆生,想學前端快點找工作應該如何去學。 對於畢業生來說,最要緊的事情就是快點找到工作。所以你學前端的時候就抓重點來學,因為很多東西,工作上用不到,所以學了也沒必 ...
  • 前提: (1) 相關博文地址: SpringBoot + Vue + ElementUI 實現後臺管理系統模板 -- 前端篇(一):搭建基本環境:https://www.cnblogs.com/l-y-h/p/12930895.html SpringBoot + Vue + ElementUI 實現 ...
  • 迭代器模式是一種使用頻率非常高的設計模式,迭代器用於對一個聚合對象進行遍歷。通過引入迭代器可以將數據的遍歷功能從聚合對象中分離出來,聚合對象只負責存儲數據,聚合對象只負責存儲數據,而遍曆數據由迭代器來完成。 模式動機 一個聚合對象,如一個列表(List)或者一個集合(Set),應該提供一種方法來讓別 ...
  • 代理的本質無論任何時候,只要談到設計模式,大腦中一定要蹦出這四個字“活學活用”。要想對某個事物做到活學活用,必須要對它足夠瞭解,甚至要剖析到本質才行。總是會有些人說,我幹嘛要知道原理,幹嘛要去看源碼?會用就行了。對於這種情況,我只有五個字相送,“你開心就好”。不可否認,認識一個陌生事物,大部分情況還 ...
  • 一、JML初探 ​ 作為一種形式化語言,可以約束 代碼中類和方法的狀態和行為形成規格,通過將一系列具體代碼實現抽象成明確的行為介面,可以形成一種契約式編程模式, 設計者無需考慮實際的數據結構與演算法,可以聚焦於程式的整體邏輯, 形式化語言的無二義性能讓實現者準確理解介面功能,根據問題需要選擇合適的實現 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...