工作五年了,居然還不懂門面模式!
大家好,我是老田,從今天開始,本公眾號每周給大家送福利,送什么呢?肯定是技術(shù)書啦,不搞那么多花里胡哨的,參與方式見文末。
好啦,進(jìn)入我們的主題,今天我給大家分享設(shè)計模式中的門面模式。用貼切的生活故事,以及真實項目場景來講設(shè)計模式,最后用一句話來總結(jié)這個設(shè)計模式。
故事
開發(fā)的朋友都知道,后端開發(fā)通常都是:
controller---servie---dao/mapper/repository
但是,我問過很多人,熟悉門面模式不?有的工作五年了都不知道。
今天老田,就帶你來看看門面模式。
門面模式概述
門面模式(Facade Pattern)又叫作外觀模式,提供了一個統(tǒng)一的接口,用來訪問子系統(tǒng)中的一群接口。其主要特征是定義了一個高層接口,讓子系統(tǒng)更容易使用,屬于結(jié)構(gòu)型設(shè)計模式。
英文:
Provide a unified interface to a set of interfaces in asubsystem.Facade defines a higher-level interface that makes thesubsystem easier to use.
其實,在日常編碼工作中,我們都在有意無意地大量使用門面模式。但凡只要高層模塊需要調(diào)度多個子系統(tǒng)(2個以上類對象),我們都會自覺地創(chuàng)建一個新類封裝這些子系統(tǒng),提供精簡的接口,讓高層模塊可以更加容易地間接調(diào)用這些子系統(tǒng)的功能。
生活中的案例
關(guān)于門面模式,在生活中的案例,非常之多。
案例1:去銀行辦理業(yè)務(wù),有個前臺接待你,然后,這個前臺會問你需要辦什么業(yè)務(wù),他會一個一個帶你辦理,這樣我們就不需要到處亂串、到處找對應(yīng)業(yè)務(wù)窗口了。這個前臺人員就相當(dāng)于門面模式。
案例2:我們建房子,如果沒有包工頭的話,那就是你自己要去找水泥工,電工、裝修工等。但如果有了包工頭,這些活你都不用干了,直接跟包工頭說,需要電工來把線路搞好。這個包工頭就可以理解為門面模式。
案例3:我們后端開發(fā)的controller,也可以理解為門面模式,比如說獲取用戶賬戶信息,先查UserService獲取用戶信息,然后查UserAccountService用戶賬戶信息。
門面模式適用場景
在軟件系統(tǒng)中,門面模式適用于以下應(yīng)用場景。
- 為一個復(fù)雜的模塊或子系統(tǒng)提供一個簡潔的供外界訪問的接口。
 - 希望提高子系統(tǒng)的獨立性時。
 - 當(dāng)子系統(tǒng)由于不可避免的暫時原因?qū)е驴赡艽嬖贐ug或性能相關(guān)問題時,可以通過門面模式提供一個高層接口,隔離客戶端與子系統(tǒng)的直接交互,預(yù)防代碼污染。
 
門面模式通用寫法
還是使用代碼來實現(xiàn)一個簡單的門面模式,因為咱們最喜歡的就是從demo開始。
業(yè)務(wù)場景:現(xiàn)在需要調(diào)用三個service的各自的方法:
- public class ServiceA {
 - public void doA(){
 - System.out.println("do ServiceA");
 - }
 - }
 - public class ServiceB {
 - public void doB(){
 - System.out.println("do ServiceB");
 - }
 - }
 - public class ServiceC {
 - public void doC(){
 - System.out.println("do ServiceC");
 - }
 - }
 
在沒有引入門面模式的時候,客戶端是這么調(diào)用的:
- public class Client {
 - public static void main(String[] args) {
 - ServiceA serviceA=new ServiceA();
 - ServiceB serviceB=new ServiceB();
 - ServiceC serviceC=new ServiceC();
 - serviceA.doA();
 - serviceB.doB();
 - serviceC.doC();
 - }
 - }
 
每次,客戶端自己都需要創(chuàng)建很多service對象,如果涉及到有很多個service,那這代碼不是很尷尬嗎?會出現(xiàn)大量重復(fù)性的代碼。
運(yùn)行結(jié)果
- do ServiceA
 - do ServiceB
 - do ServiceC
 
下面我們就來加入門面模式:
- public class Facade {
 - //是不是很像我們controller里注入各種service?
 - private ServiceA serviceA = new ServiceA();
 - private ServiceB serviceB = new ServiceB();
 - private ServiceC serviceC = new ServiceC();
 - public void doA() {
 - serviceA.doA();
 - }
 - public void doB() {
 - serviceB.doB();
 - }
 - public void doC() {
 - serviceC.doC();
 - }
 - }
 
客戶端變成了:
- public class Client {
 - public static void main(String[] args) {
 - //輕輕松松的搞定,只需要創(chuàng)建門面這個對象即可
 - Facade facade=new Facade();
 - facade.doA();
 - facade.doB();
 - facade.doC();
 - }
 - }
 
運(yùn)行結(jié)果:
- do ServiceA
 - do ServiceB
 - do ServiceC
 
門面模式UML圖
結(jié)合這個UML圖,在回顧銀行前臺人員和包工頭的案例,就更輕松的理解門面模式了。
門面模式中的角色
由上圖可以看到,門面模式主要包含2個角色。
- 外觀角色(Facade):也叫作門面角色,是系統(tǒng)對外的統(tǒng)一接口。
 - 子系統(tǒng)角色(Service):可以同時有一個或多個Service。每個Service都不是一個單獨的類,而是一個類的集合。Service們并不知道Facade的存在,對于Service們而言,F(xiàn)acade 只是另一個客戶端而已(即Facade對ServiceA、ServiceB、ServiceC透明)。
 
門面模式的擴(kuò)展
優(yōu)點
● 減少系統(tǒng)的相互依賴 想想看,如果我們不使用門面模式,外界訪問直接深入到子系統(tǒng)內(nèi)部,相互之間是一種強(qiáng)耦合關(guān)系,你死我就死,你活我才能活,這樣的強(qiáng)依賴是系統(tǒng)設(shè)計所不能接受的,門面模式的出現(xiàn)就很好地解決了該問題,所有的依賴都是對門面對象的依賴,與子系統(tǒng)無關(guān)。
● 提高了靈活性 依賴減少了,靈活性自然提高了。不管子系統(tǒng)內(nèi)部如何變化,只要不影響到門面對象,任你自由活動。
● 提高安全性 想讓你訪問子系統(tǒng)的哪些業(yè)務(wù)就開通哪些邏輯,不在門面上開通的方法,你休想訪問到 。
缺點
當(dāng)增加子系統(tǒng)和擴(kuò)展子系統(tǒng)行為時,可能容易帶來未知風(fēng)險。
不符合開閉原則。
某些情況下,可能違背單一職責(zé)原則。
大神們是如何使用的
在Spring中也是有大量使用到門面模式,比如說
- org.springframework.jdbc.support.JdbcUtils
 
再來看看其中的方法
- public static void closeConnection(@Nullable Connection con) {
 - con.close();
 - }
 - public static Object extractDatabaseMetaData(DataSource dataSource, DatabaseMetaDataCallback action)
 - throws MetaDataAccessException {
 - Connection con = null;
 - try {
 - con = DataSourceUtils.getConnection(dataSource);
 - DatabaseMetaData metaData = con.getMetaData();
 - if (metaData == null) {
 - //.....
 - }
 - return action.processMetaData(metaData);
 - }
 - }
 - ......
 
都是給我封裝好了方法,對于我們開發(fā)者來說,我只面向JdbcUtils這一個類就好了,我不用去管Connection、ResultSet等是怎么創(chuàng)建的,需要的時候,我調(diào)用JdbcUtils的對應(yīng)方法即可獲得對應(yīng)的對象。
在Mybatis中也是用到了門面模式,比如:
- org.apache.ibatis.session.Configuration
 
在Configuration中以new開頭的方法,比如:
- public Executor newExecutor(Transaction transaction) {
 - return newExecutor(transaction, defaultExecutorType);
 - }
 - public MetaObject newMetaObject(Object object) {
 - return MetaObject.forObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
 - }
 - public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
 - ...
 - return parameterHandler;
 - }
 - public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
 - ResultHandler resultHandler, BoundSql boundSql) {
 - ...
 - return resultSetHandler;
 - }
 - public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement){
 - ...
 - }
 
對于調(diào)用這些方法的地方,他并不知道是怎么new出來的對象,只管使用就行了。
在Tomcat中也有門面模式,比如:
- org.apache.catalina.connector.RequestFacade
 
從名字就知道它用了門面模式。它封裝了非常多的request操作,也整合了很多servlet-api以外的內(nèi)容,給用戶使用提供了很大便捷。同樣,Tomcat針對Response和Session也封裝了對應(yīng)的ResponseFacade類和StandardSessionFacade類,感興趣的小伙伴可以深入了解一下。
PS:基本上所有以Facade結(jié)尾的類,都是使用到了門面模式。
參考:tom的設(shè)計模式課程
總結(jié)
好了,關(guān)于門面模式就分享這么多,看完本文是不是覺得門面模式其實很簡單的,另外在工作也可以考慮是不是可以把它用上,同時,面試的時候也可以用來吹吹。
最后用一句話來總結(jié)門面模式:
打開一扇門,走向全世界。
本文轉(zhuǎn)載自微信公眾號「Java后端技術(shù)全棧」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系Java后端技術(shù)全棧公眾號。

















 
 
 












 
 
 
 