《HeadFirst設計模式》讀書筆記-第7章v2-外觀模式

定義

外觀模式(facade pattern)提供了一個統一的接口,用來訪問子系統中的一羣接口。外觀定義了一個高層接口,讓子系統更加容易使用。

這裏寫圖片描述

從上面的圖可以看出,Facade類對子系統進行了一下封裝,客戶只需要和Facade類打交道,不需要接觸子系統中的各個類,也不需要了解子系統中各個類間的關係。從這個角度來說,客戶也就與子系統解耦了,不需要依賴於子系統中具體的類了。

外觀模式的一個重要的目的就是簡化系統,提供給客戶一個簡單的接口,方便客戶使用。

代碼實現

例子是爲家庭影院構造外觀,讓觀看電影的步驟儘可能地簡單。下圖給出了類圖。

這裏寫圖片描述

首先看看HomeTheaterFacade這個高層接口,它提供了6個接口分別執行6個動作,每個動作是通過調用子系統中其它的類來實現的。

public class HomeTheaterFacade {
    Amplifier amp; // 功放
    Tuner tuner; // 電子調音器
    DvdPlayer dvd; //
    CdPlayer cd; //
    Projector projector; // 投影機
    TheaterLights lights; // 劇場燈光
    Screen screen; // 屏幕
    PopcornPopper popper; // 爆米花機

    public HomeTheaterFacade(Amplifier amp, Tuner tuner, DvdPlayer dvd,
            CdPlayer cd, Projector projector, Screen screen,
            TheaterLights lights, PopcornPopper popper) {
        this.amp = amp;
        this.tuner = tuner;
        this.dvd = dvd;
        this.cd = cd;
        this.projector = projector;
        this.screen = screen;
        this.lights = lights;
        this.popper = popper;
    }

    // 開始觀看名爲“movie”的電影接口
    public void watchMovie(String movie) {
        System.out.println("Get ready to watch a movie...");
        popper.on(); // 打開爆米花機
        popper.pop(); // 開始爆米花
        lights.dim(10); // 調低燈光亮度爲10%
        screen.down(); // 放下屏幕
        projector.on(); // 打開投影機
        projector.wideScreenMode(); // 設置寬屏模式
        amp.on(); // 打開功放
        amp.setDvd(dvd); 
        amp.setSurroundSound(); // 設置環繞立體聲
        amp.setVolume(5); // 設置音量
        dvd.on();  // 打開DVD播放器
        dvd.play(movie); // 開始播放電影
    }

    public void endMovie() {
        System.out.println("Shutting movie theater down...");
        popper.off();
        lights.on();
        screen.up();
        projector.off();
        amp.off();
        dvd.stop();
        dvd.eject(); // 彈出Dvd播放器
        dvd.off();
    }

    public void listenToCd(String cdTitle) {
        System.out.println("Get ready for an audiopile experence...");
        lights.on();
        amp.on();
        amp.setVolume(5);
        amp.setCd(cd);
        amp.setStereoSound();
        cd.on();
        cd.play(cdTitle);
    }

    public void endCd() {
        System.out.println("Shutting down CD...");
        amp.off();
        amp.setCd(cd);
        cd.eject();
        cd.off();
    }

    public void listenToRadio(double frequency) {
        System.out.println("Tuning in the airwaves...");
        tuner.on();
        tuner.setFrequency(frequency);
        amp.on();
        amp.setVolume(5);
        amp.setTuner(tuner);
    }

    public void endRadio() {
        System.out.println("Shutting down the tuner...");
        tuner.off();
        amp.off();
    }
}

因爲子系統中類太多了,這裏只列出幾個,其它的類似,不影響理解模式。

Amplifier.java

public class Amplifier {
    String description;
    Tuner tuner;
    DvdPlayer dvd;
    CdPlayer cd;

    public Amplifier(String description) {
        this.description = description;
    }

    public void on() {
        System.out.println(description + " on");
    }

    public void off() {
        System.out.println(description + " off");
    }

    public void setStereoSound() {
        System.out.println(description + " stereo mode on");
    }

    public void setSurroundSound() {
        System.out.println(description + " surround sound on (5 speakers, 1 subwoofer)");
    }

    public void setVolume(int level) {
        System.out.println(description + " setting volume to " + level);
    }

    public void setTuner(Tuner tuner) {
        System.out.println(description + " setting tuner to " + dvd);
        this.tuner = tuner;
    }

    public void setDvd(DvdPlayer dvd) {
        System.out.println(description + " setting DVD player to " + dvd);
        this.dvd = dvd;
    }

    public void setCd(CdPlayer cd) {
        System.out.println(description + " setting CD player to " + cd);
        this.cd = cd;
    }

    public String toString() {
        return description;
    }
}

CdPlayer.java

public class CdPlayer {
    String description;
    int currentTrack;
    Amplifier amplifier;
    String title;

    public CdPlayer(String description, Amplifier amplifier) {
        this.description = description;
        this.amplifier = amplifier;
    }

    public void on() {
        System.out.println(description + " on");
    }

    public void off() {
        System.out.println(description + " off");
    }

    public void eject() {
        title = null;
        System.out.println(description + " eject");
    }

    public void play(String title) {
        this.title = title;
        currentTrack = 0;
        System.out.println(description + " playing \"" + title + "\"");
    }

    public void play(int track) {
        if (title == null) {
            System.out.println(description + " can't play track " + currentTrack + 
                    ", no cd inserted");
        } else {
            currentTrack = track;
            System.out.println(description + " playing track " + currentTrack);
        }
    }

    public void stop() {
        currentTrack = 0;
        System.out.println(description + " stopped");
    }

    public void pause() {
        System.out.println(description + " paused \"" + title + "\"");
    }

    public String toString() {
        return description;
    }
}

Screen.java

public class Screen {
    String description;

    public Screen(String description) {
        this.description = description;
    }

    public void up() {
        System.out.println(description + " going up");
    }

    public void down() {
        System.out.println(description + " going down");
    }

    public String toString() {
        return description;
    }
}

測試驅動代碼,也是客戶代碼:

public class HomeTheaterTestDrive {
    public static void main(String[] args) {
        Amplifier amp = new Amplifier("Top-O-Line Amplifier");
        Tuner tuner = new Tuner("Top-O-Line AM/FM Tuner", amp);
        DvdPlayer dvd = new DvdPlayer("Top-O-Line DVD Player", amp);
        CdPlayer cd = new CdPlayer("Top-O-Line CD Player", amp);
        Projector projector = new Projector("Top-O-Line Projector", dvd);
        TheaterLights lights = new TheaterLights("Theater Ceiling Lights");
        Screen screen = new Screen("Theater Screen");
        PopcornPopper popper = new PopcornPopper("Popcorn Popper");

        HomeTheaterFacade homeTheater = 
                new HomeTheaterFacade(amp, tuner, dvd, cd, 
                        projector, screen, lights, popper);
        // 客戶開始/停止看電影只要調用高層接口的Api即可
        // 不需要和子系統中其它類交互
        homeTheater.watchMovie("Raiders of the Lost Ark");
        homeTheater.endMovie();
    }
}

該模式體現了哪些OO原則

  1. 原則3: 多用組合,少用繼承

    HomeTheaterFacade組合了子系統中各個對象,來實現對客戶來說更加友好對接口。

  2. 原則7: 最少知識原則

    客戶代碼只要調用HomeTheaterFacade的方法,只依賴於它。不管子系統內部實現怎麼變化,只要HomeTheaterFacade的接口沒有變化,客戶代碼就不需要變化。

本章總結

  1. 外觀模式不只是簡化了子系統的接口,也將客戶從組件的子系統中解耦

  2. 外觀只是對子系統的接口進行了更高層次的抽象,客戶還是可以直接調用子系統中的類的,當然一般情況這是不必要的,只要外觀接口定義的好

  3. 外觀模式符合“最少知識原則”

  4. 適配器模式和外觀模式的差別:

    兩種模式都是對其它的接口的再次封裝。差別在適配器強調對一個或者多個接口的轉換,而外觀模式則強調簡化接口

發佈了54 篇原創文章 · 獲贊 13 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章