外觀模式

定義

外觀(Facade)模式是一種通過爲多個複雜的子系統提供一個統一的接口,而使這些子系統更加容易被訪問的模式。該模式對外有一個統一接口,外部應用程序不用關心內部子系統的具體的細節,這樣會大大降低應用程序的複雜度,提高了程序的可維護性。

外觀模式屬於對象結構型模式。


要點

外觀(Facade)模式是“迪米特法則”的典型應用,優點:

  • 降低了子系統與客戶端之間的耦合度,使得子系統的變化不會影響調用它的客戶類。
  • 對客戶屏蔽了子系統組件,減少了客戶處理的對象數目,並使得子系統使用起來更加容易。
  • 降低了大型軟件系統中的編譯依賴性,簡化了系統在不同平臺之間的移植過程,因爲編譯一個子系統不會影響其他的子系統,也不會影響外觀對象。

外觀(Facade)模式的缺點:

  • 不能很好地限制客戶使用子系統類。
  • 增加新的子系統可能需要修改外觀類或客戶端的源代碼,違背了“開閉原則”。

外觀(Facade)模式包含以下主要角色:
外觀(Facade):爲多個子系統對外提供一個共同的接口。
子系統(Sub System):實現系統的部分功能,客戶可以通過外觀角色訪問它。
客戶(Client):通過一個外觀角色訪問各個子系統的功能。

在這裏插入圖片描述

場景

組建一個家庭影院,DVD 播放器、投影儀、自動屏幕、環繞立體聲、要求完成使用家庭影院的功能:

  1. 放下屏幕
  2. 開投影儀
  3. 開音響
  4. 開DVD
  5. 調暗燈光
  6. 觀影結束後,關閉各種設備

其過程爲: 直接用遙控器統籌各設備開關,簡化用戶操作步驟。


實現

Screen

/**
 * 電影幕布
 */
public class Screen {

    public void up() {
        System.out.println("升起電影幕布...");
    }

    public void down() {
        System.out.println("放下電影幕布...");
    }

}

Projector

/**
 * 投影儀
 */
public class Projector {

    public void on() {
        System.out.println("打開投影儀...");
    }

    public void off() {
        System.out.println("關閉投影儀...");
    }

    public void focus() {
        System.out.println("調節投影儀焦距...");
    }

}

Audio

/**
 * 音響
 */
public class Audio {

    public void on() {
        System.out.println("打開音響...");
    }

    public void off() {
        System.out.println("關閉音響...");
    }

    public void setVolume() {
        System.out.println("設置音量...");
    }

}

DVD

/**
 * DVD播放器
 */
public class DVD {

    public void on() {
        System.out.println("打開DVD播放器...");
    }

    public void off() {
        System.out.println("關閉DVD播放器...");
    }

    public void pause() {
        System.out.println("DVD播放暫停...");
    }

    public void play() {
        System.out.println("DVD開始播放...");
    }

    public void setDisk(String movieName) {
        System.out.println(String.format("指定DVD影碟:[ %s ]", movieName));
    }

}

Light

/**
 * 燈光
 */
public class Light {

    public void turnUp() {
        System.out.println("調亮燈光...");
    }

    public void turnDown() {
        System.out.println("調暗燈光...");
    }

}

CinemaRemoteControlFacade

/**
 * 影院集成遙控控制(外觀角色)
 */
public class CinemaRemoteControlFacade {

    private Screen screen;
    private Projector projector;
    private Audio audio;
    private DVD dvd;
    private Light light;

    public CinemaRemoteControlFacade(Screen screen, Projector projector, Audio audio, DVD dvd, Light light) {
        this.screen = screen;
        this.projector = projector;
        this.audio = audio;
        this.dvd = dvd;
        this.light = light;
    }

    /**
     * 開始看電影
     */
    public void watchMovie(String movieName) {
        screen.down();
        projector.on();
        projector.focus();
        audio.on();
        audio.setVolume();
        dvd.on();
        dvd.setDisk(movieName);
        dvd.play();
        light.turnDown();
    }

    /**
     * 結束看電影
     */
    public void endMovie() {
        dvd.off();
        audio.off();
        projector.off();
        light.turnUp();
        screen.up();
    }

}

Client

public class Client {

    public static void main(String[] args) {

        CinemaRemoteControlFacade remoteControl = new CinemaRemoteControlFacade(new Screen(), new Projector(), new Audio(), new DVD(), new Light());

		System.out.println("------------準備播放電影------------\n");
		remoteControl.watchMovie("決戰中途島");
        
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
		System.out.println("\n------------電影播放結束------------\n");
        remoteControl.endMovie();

    }

}

==================================
輸出:

------------準備播放電影------------

放下電影幕布...
打開投影儀...
調節投影儀焦距...
打開音響....
設置音量...
打開DVD播放器...
指定DVD影碟:[ 決戰中途島 ]
DVD開始播放...
調暗燈光...

------------播放結束------------

關閉DVD播放器...
關閉音響...
關閉投影儀...
調亮燈光...
升起電影幕布...

源碼

Click here


總結

  1. 外觀模式對外屏蔽了子系統的細節,因此外觀模式降低了客戶端對子系統使用的複雜性
  2. 外觀模式對客戶端與子系統的耦合關係 - 解耦,讓子系統內部的模塊更易維護和擴展
  3. 通過合理的使用外觀模式,可以幫我們更好的劃分訪問的層次
  4. 當系統需要進行分層設計時,可以考慮使用 Facade 模式
  5. 在維護一個遺留的大型系統時,可能這個系統已經變得非常難以維護和擴展,此時可以考慮爲新系統開發一個 Facade 類,來提供遺留系統的比較清晰簡單的接口,讓新系統與 Facade 類交互,提高複用性
  6. 不能過多的或者不合理的使用外觀模式,使用外觀模式好,還是直接調用模塊好。要以讓系統有層次,利於維護爲目的。

在外觀模式中,當增加或移除子系統時需要修改外觀類,這違背了“開閉原則”。如果引入抽象外觀類,讓具體外觀類繼承抽象外觀類,具體外觀類聚合使用需要的子系統即可。


Tomcat中有很多場景都使用到了外觀模式,因爲Tomcat中有很多不同的組件,每個組件需要相互通信,但又不能將自己內部數據過多地暴露給其他組件。用外觀模式隔離數據是個很好的方法,比如Request上使用外觀模式,因爲Request類中很多方法都是組件內部之間交互用的,比如setComet、setReuqestedSessionId等方法,這些方法並不對外公開,但又必須設置爲public,因爲還要和內部組件交互使用。最好的解決方法就是通過使用一個Facade類,屏蔽掉內部組件之間交互的方法,只提供外部程序要使用的方法。

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