Java設計模式系列之——外觀模式

故事一

A:聽說最近出了某款遊戲超好玩,但是我電腦帶不起來,需要個組裝機玩遊戲。

B:你說XX遊戲嗎?真的很好玩,筆記本玩不了,我就是用的組裝機玩的呢。

A:是嗎?我對配置這些都不太懂,我得先去研究,然後還需要花時間從CPU、內存、主板、顯示器等等這些一家家跑,再回來組裝。

B:哪需要這麼麻煩啊,你根本不用一個個去跑着買回來組裝啊,又耽誤時間又浪費精力。你不知道有專門的組裝電腦的賣家嗎?直接去找他們,把你想要的配置告訴他們就好了,其他你都不用管,他們會把組裝好的電腦直接送給你。

A:真的嗎?那你有聯繫方式嗎,給我一下,我找他們幫我組裝電腦。

過了幾天後...

A:哇、小B,太感謝你啦,我上次聯繫了組裝電腦的賣家,把我想要的配置告訴他們以後,他們當天就把組裝好的電腦給我送來了,效率太高了,我當天晚上就玩了那款超火的遊戲,還帶妹子玩的飛起,哈哈!

B:太陽,你又裝我,晚上單挑,我虐死你

故事二

A和B都是從外地到杭州打工的青年,A最近剛好搬家到了一個新小區,居住證也在一個星期後到期,需要續簽,中午喫飯的時候和B聊到這個事。

B:聽說你搬家了?

A:是啊,昨天剛搬,社區週末不上班,還沒來得及去登記呢,居住證也快到期了,還要去打印最近半年社保證明,再去派出所辦理續簽,請一上午假都不知道夠不夠?

B:我就說你平時就知道打遊戲,其他事都不關心,杭州早就實行"最多跑一趟"的服務了,早就不用一個地方一個地方去跑啦,直接去XX派出所就行啦,他們會幫你登記好,住址改簽,會幫你打印好社保證明,給你把居住證續簽。

A:真的嗎?那我只要去派出所就行了,實在太方便了,謝謝你啊!

B:可不是嘛,像什麼換駕照啊、車牌啊這些,都不用像以前那樣一個地方一個地方跑那麼麻煩了,只需要去一個地方,就能一次性把事情辦好。我說你平時真的要少玩點遊戲,如果想在杭州定居,你要對這些政策多關係一點,以後少跑點彎路。

A:嘿嘿,都被你說的有點不好意思了,難怪你小子都快結婚了,是有原因的啊,哈哈!

B:別別,你是憑實力單身,在下比不了,服!

上面兩個都是會經常發生在我們身邊的故事,我們從抽象的角度來審視一下:

  1. 故事一A作爲我們的客戶端,電腦城作爲我們一個龐大的主系統,cpu、主板、內存這些店作爲一個個系統子模塊,那麼組裝電腦的店是什麼角色呢?

  2. 故事二A依然作爲客戶端,人口檔案管理作爲一個龐大的主系統,社區、社保局、銀行等作爲一個個系統子模塊,那麼只需要跑一趟的派出所是什麼角色呢?

下面,進入我們今天的主題,JAVA設計模式之——外觀模式。

什麼是外觀模式

Facade Pattern: Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use.

外觀模式(Facade Pattern):爲子系統中的一組接口提供一個一致的界面,外觀模式定義了一個高層接口,這個接口使得這一子系統更加容易使用。外觀模式又稱爲門面模式,它是一種對象結構型模式。

通俗點說,就是通過引入一個外觀角色,來簡化客戶端與子系統之間的交互,爲複雜的子系統調用提供一個統一的入口,降低子系統與客戶端的耦合度,且客戶端調用子系統更加方便。正如前言故事中的電腦組裝店和一站式服務派出所。他們就是充當了外觀角色。

外觀模式的兩個角色

  • Facade(外觀角色):在客戶端可以調用它的方法,在外觀角色中可以知道相關的(一個或者多個)子系統的功能和責任;在正常情況下,它將所有從客戶端發來的請求委派到相應的子系統去,傳遞給相應的子系統對象處理。

  • SubSystem(子系統角色):在軟件系統中可以有一個或者多個子系統角色,每一個子系統可以不是一個單獨的類,而是一個類的集合,它實現子系統的功能;每一個子系統都可以被客戶端直接調用,或者被外觀角色調用,它處理由外觀類傳過來的請求;子系統並不知道外觀的存在,對於子系統而言,外觀角色僅僅是另外一個客戶端而已。

外觀模式的UML圖

外觀模式代碼實例

我們以轎車爲例,以往的汽車,我們用鑰匙點火後,白天要自己開日行燈,晚上要手動開大燈,霧天手動開霧燈,冷熱也要自己開空調,還有下雨要開雨刮器等等,雖然我們都可以一個個去操作控制,但是高峯期路況太多,很多人反應不過來,這已經不是行車不規範,親人兩行淚能控制的了。

於是萬能的人類發明了一鍵啓動按鈕,只要按下這個按鈕,就會啓動Carplay、雨刮器自動感應,空調自動調節、行車燈自動感應功能。這其實也是外觀模式的應用,下面我們看看代碼實例。

1、定義子系統類Carplay

package com.weiya.mazhichu.designpatterns.facade;

/**
 * <p class="detail">
 * 功能:子系統類:Carplay
 * </p>
 *
 * @author Moore
 * @ClassName Car play.
 * @Version V1.0.
 * @date 2019.07.31 15:21:12
 */
public class CarPlay {

    public void openCarPlay(){
        System.out.println("carplay開啓,可以投影手機導航,或者聽歌了");
    }

    public void closeCarPlay(){
        System.out.println("carplay關閉");
    }
}

2、定義子系統類空調

package com.weiya.mazhichu.designpatterns.facade;

/**
 * <p class="detail">
 * 功能:子系統類:空調
 * </p>
 *
 * @author Moore
 * @ClassName Air conditioning.
 * @Version V1.0.
 * @date 2019.07.31 15:24:16
 */
public class AirConditioning {
    public void openAirConditioningAutoSwitch(){
        System.out.println("空調自動調節開啓");
    }

    public void closeAirConditioning(){
        System.out.println("空調關閉");
    }
}

3、定義子系統類行車燈

package com.weiya.mazhichu.designpatterns.facade;

/**
 * <p class="detail">
 * 功能: 子系統類:行車燈
 * </p>
 *
 * @author Moore
 * @ClassName Driving lamp.
 * @Version V1.0.
 * @date 2019.07.31 15:17:15
 */
public class DrivingLamp {

    public void openAutoSwitchLamp(){
        System.out.println("行車燈自動感應切換開啓");
    }

    public void turnOffLamp(){
        System.out.println("行車燈自動感應關閉");
    }
}

4、定義子系統類雨刮器

package com.weiya.mazhichu.designpatterns.facade;

/**
 * <p class="detail">
 * 功能:子系統類:雨刮器
 * </p>
 *
 * @author Moore
 * @ClassName Wiper.
 * @Version V1.0.
 * @date 2019.07.31 15:26:41
 */
public class Wiper {
    public void openWiperAutoSensor(){
        System.out.println("雨刮器自動感應開啓");
    }

    public void closeWiper(){
        System.out.println("雨刮器關閉");
    }
}

5、定義外觀類(門面類):一鍵啓動按鈕

package com.weiya.mazhichu.designpatterns.facade;

/**
 * <p class="detail">
 * 功能:外觀類(核心角色):一鍵啓動按鈕
 * </p>
 *
 * @author Moore
 * @ClassName One click start button.
 * @Version V1.0.
 * @date 2019.07.31 15:30:51
 */
public class OneClickStartButton {

    private CarPlay carPlay;
    private AirConditioning airConditioning;
    private DrivingLamp drivingLamp;
    private Wiper wiper;

    public OneClickStartButton() {
        this.carPlay = new CarPlay();
        this.airConditioning = new AirConditioning();
        this.drivingLamp = new DrivingLamp();
        this.wiper = new Wiper();
    }

    /**
     * <p class="detail">
     * 功能:啓動
     * </p>
     *
     * @author Moore
     * @date 2019.07.31 15:32:15
     */
    public void start(){
        carPlay.openCarPlay();
        airConditioning.openAirConditioningAutoSwitch();
        drivingLamp.openAutoSwitchLamp();
        wiper.openWiperAutoSensor();
    }

    /**
     * <p class="detail">
     * 功能:熄火
     * </p>
     *
     * @author Moore
     * @date 2019.07.31 15:32:17
     */
    public void flameout(){
        carPlay.closeCarPlay();
        airConditioning.closeAirConditioning();
        drivingLamp.turnOffLamp();
        wiper.closeWiper();
    }
}

6、客戶端調用

package com.weiya.mazhichu.designpatterns.facade;

/**
 * <p class="detail">
 * 功能:客戶端,直接和外觀類(門面)打交道,通過外觀類訪問子系統
 * </p>
 *
 * @author Moore
 * @ClassName Client.
 * @Version V1.0.
 * @date 2019.07.31 15:36:24
 */
public class Client {

    public static void main(String[] args) {
        OneClickStartButton button = new OneClickStartButton();
        System.out.println("按下一鍵啓動按鈕,汽車啓動");
        button.start();
        System.out.println("------------------------");
        System.out.println("按下一鍵啓動按鈕,汽車熄火");
        button.flameout();
    }


}

查看運行結果:

通過示例,我們可以看出,客戶端和子系統的交互都是通過外觀類來進行的,客戶端不需要知道子系統內部的實現,只需要跟facade交互即可,降低了客戶端和子系統之間的耦合性,同時和客戶端交互的對象減少,也使得客戶端的代碼變得簡單。

可以說,外觀模式在我們日常生活中隨處可見,開發中也形影不離,像mybatis-generator,只需要通過一個外觀類,就可以自動生成dao層、mapper層、service層的代碼,不需要一級級去生成。還有tomcat、slf4j、spring jdbc等也用到外觀模式,這兒不做深入了,感興趣的同學自己去了解一下。

最後,我們來總結一下外觀模式。

優點:

  • 鬆散耦合:使得客戶端和子系統之間解耦,讓子系統內部的模塊功能更容易擴展和維護。

  • 簡單易用:客戶端根本不需要知道子系統內部的實現,或者根本不需要知道子系統內部的構成,它只需要跟Facade類交互即可。

  • 更好的劃分訪問層次:有些方法是對系統外的,有些方法是系統內部相互交互的使用的。子系統把那些暴露給外部的功能集中到門面中,這樣就可以實現客戶端的使用,很好的隱藏了子系統內部的細節。

缺點:不引入抽象外觀類的情況,違背了開閉原則。

適用場景:

  • 對於一個複雜的子系統,需要爲用戶提供一個簡單的交互操作。

  • 不希望客戶代碼和子系統中的類有耦合,以便提高子系統的獨立性和可移植性。

  • 在層次化結構中,可以使用外觀模式定義系統中每一層的入口,層與層之間不直接產生聯繫,而通過外觀類建立聯繫,降低層之間的耦合度。

 

我不能保證我寫的文章都是正確的,但是我能保證都是我自己花時間用心寫的,所有的代碼示例都是原創,所有的理解都只是我個人理解,不能代表官方權威,所以請各位讀者閱讀時帶着批判的眼光,有選擇性的認同,謝謝!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章