开发之路(设计模式八:外观模式)

改变接口的新模式,为了简化接口

这次带来的模式为外观模式,之所以这么称呼,因为它将一个或多个类复杂的一切都隐藏起来。

我依旧举生活中例子,现在有些朋友家的液晶电视可能是大尺寸的,或者有用投影仪来看电视,打游戏的。有一天我想用家庭影院系统在家里看一次大片。

1、打开爆米花机
2、开始爆米花
3、将灯光调暗
4、放下屏幕
5、打开投影仪
6、将投影仪的输入切换到DVD
7、将投影仪设置在宽屏模式下
8、打开功放
9、将功放的输入设置为DVD
10、将功放的设置为环绕立体声
11、功放音量调到中(5)
12、打开DVD播放器
13、开始播放DVD

等我操作完这些我才能开始享受大片,需要打开这么多开关。。而且如果看完要关闭是不是又要反向把动作再来一遍,这样或许连我都没兴趣使用影院系统了。也许这套系统该升级了!!

这时候外观模式就发挥作用了,通过实现一个提供更合理的接口的外观类,让一个复杂的系统变得使用简单了,
我们来看看外观是怎么运作的。如图(可点击查看原图)
《开发之路(设计模式八:外观模式)》

这里注意:
1、外观不只是简化了接口,也将客户从组件的子系统中解耦,若客户有需要还是可以使用子系统,外观是为了简化接口。
2、外观和适配器模式可以包装许多类,但是外观的意图是简化接口,而适配器的意图是将接口转换成不同的接口,换句话说是让两个原本不适用的东西,变得能相互使用一样。

现在开始我们的家庭影院系统升级把~~~~

家庭影院类(外观)

package headfirst.facade.hometheater;

/**
 * 家庭影院 (将各个组件统一的“接口”)
 * 
 * @author Joy
 * 
 */
// 外观类
public class HomeTheaterFacade {
    /**
     * 组合,将用到的子系统组件全部都在这里
     */
    Amplifier amp;// 扩音器
    Tuner tuner;// 无线电收音机
    DvdPlayer dvd;// DVD播放器
    CdPlayer cd;// 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;
    }

    /**
     * 将子系统的组件整合成一个统一的接口 watchMovie()方法 就相当于把所有操作放在一个方法里,简化操作
     * 将我们之前手动进行的每项任务我依次处理。 注意:每项任务都是具体实现都是委托子系统中相应的组件处理的
     * 
     * @param movie
     */
    public void watchMovie(String movie) {
        System.out.println("准备好,看电影了...");
        popper.on();
        popper.pop();
        lights.dim(10);
        screen.down();
        projector.on();
        projector.wideScreenMode();
        amp.on();
        amp.setDvd(dvd);
        amp.setSurroundSound();
        amp.setVolume(5);
        dvd.on();
        dvd.play(movie);
    }

    /**
     * endMovie() 负责关闭一切,每项任务也都是委托子系统中对应组件的具体方法
     */
    public void endMovie() {
        System.out.println("关闭电影...");
        popper.off();
        lights.on();
        screen.up();
        projector.off();
        amp.off();
        dvd.stop();
        dvd.eject();
        dvd.off();
    }

    /**
     * 下面这项方法,若客户还是需要调用子系统的方法话,相应调用即可
     * 
     * @param cdTitle
     */
    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();
    }
}

下面是各个组件类(子系统)

package headfirst.facade.hometheater;

/**
 * 扩音器类
 * 
 * @author Joy
 * 
 */
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 + " 开启");
    }

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

    public void setStereoSound() {
        System.out.println(description + " 环绕立体模式");
    }

    public void setSurroundSound() {
        System.out.println(description + " 环绕声设置(5 音量, 1 低音炮)");
    }

    public void setVolume(int level) {
        System.out.println(description + " 设置音量: " + level);
    }

    public void setTuner(Tuner tuner) {
        System.out.println(description + "设置音效 " + dvd);
        this.tuner = tuner;
    }

    public void setDvd(DvdPlayer dvd) {
        System.out.println(description + " 设置DVD播放器 " + dvd);
        this.dvd = dvd;
    }

    public void setCd(CdPlayer cd) {
        System.out.println(description + " 设置CD播放器" + cd);
        this.cd = cd;
    }

    public String toString() {
        return description;
    }
}
package headfirst.facade.hometheater;

/**
 * CD播放器
 * 
 * @author Joy
 * 
 */
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 + " 开启");
    }

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

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

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

    public String toString() {
        return description;
    }
}
package headfirst.facade.hometheater;

/**
 * DVD播放器
 * 
 * @author Joy
 * 
 */
public class DvdPlayer {
    String description;
    int currentTrack;
    Amplifier amplifier;
    String movie;

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

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

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

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

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

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

    public void stop() {
        currentTrack = 0;
        System.out.println(description + " 停止 \"" + movie + "\"");
    }

    public String toString() {
        return description;
    }
}
package headfirst.facade.hometheater;

/**
 * 爆米花机类
 * 
 * @author Joy
 * 
 */
public class PopcornPopper {
    String description;

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

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

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

    public void pop() {
        System.out.println(description + "正在工作!");
    }

    public String toString() {
        return description;
    }
}
package headfirst.facade.hometheater;
/**
 * 投影仪类
 * @author Joy
 *
 */
public class Projector {
    String description;
    DvdPlayer dvdPlayer;
    
    public Projector(String description, DvdPlayer dvdPlayer) {
        this.description = description;
        this.dvdPlayer = dvdPlayer;
    }
 
    public void on() {
        System.out.println(description + " 开启");
    }
 
    public void off() {
        System.out.println(description + " 关闭");
    }

    public void wideScreenMode() {
        System.out.println(description + " 设置成宽屏模式 (16x9 高宽比)");
    }

    public void tvMode() {
        System.out.println(description + " 设置为TV模式 (4x3 高宽比)");
    }
  
        public String toString() {
                return description;
        }
}
package headfirst.facade.hometheater;

/**
 * 屏幕类
 * 
 * @author Joy
 * 
 */
public class Screen {
    String description;

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

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

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

    public String toString() {
        return description;
    }
}
package headfirst.facade.hometheater;

/**
 * 影院灯光
 * 
 * @author Joy
 * 
 */
public class TheaterLights {
    String description;

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

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

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

    public void dim(int level) {
        System.out.println(description + " 掉亮度为: " + level + "%");
    }

    public String toString() {
        return description;
    }
}
package headfirst.facade.hometheater;

public class Tuner {
    String description;
    Amplifier amplifier;
    double frequency;

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

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

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

    public void setFrequency(double frequency) {
        System.out.println(description + " setting frequency to " + frequency);
        this.frequency = frequency;
    }

    public String toString() {
        return description;
    }
}

测试类(用轻松方式看电影)

package TestMain;

import headfirst.facade.hometheater.Amplifier;
import headfirst.facade.hometheater.CdPlayer;
import headfirst.facade.hometheater.DvdPlayer;
import headfirst.facade.hometheater.HomeTheaterFacade;
import headfirst.facade.hometheater.PopcornPopper;
import headfirst.facade.hometheater.Projector;
import headfirst.facade.hometheater.Screen;
import headfirst.facade.hometheater.TheaterLights;
import headfirst.facade.hometheater.Tuner;

/**
 * 外观作用简化接口 
 * 适配器作用是将接口能与另一个接口相匹配
 * 
 * @author Joy
 * 
 */
public class HomeTheaterTestDrive {
    public static void main(String[] args) {
        // 将各个组件都实例化
        Amplifier amp = new Amplifier("Top-O-Line 扩音器");
        Tuner tuner = new Tuner("Top-O-Line AM/FM 调谐器", amp);
        DvdPlayer dvd = new DvdPlayer("Top-O-Line DVD 播放器", amp);
        CdPlayer cd = new CdPlayer("Top-O-Line CD 播放器", amp);
        Projector projector = new Projector("Top-O-Line 投影仪", dvd);
        TheaterLights lights = new TheaterLights("影院天花板灯");
        Screen screen = new Screen("影院屏幕");
        PopcornPopper popper = new PopcornPopper("爆米花机");

        // 实例化外观
        HomeTheaterFacade homeTheater = new HomeTheaterFacade(amp, tuner, dvd,
                cd, projector, screen, lights, popper);
        // 使用简化的开启关闭接口
        homeTheater.watchMovie("《夺宝奇兵》");
        homeTheater.endMovie();
    }
}

效果图
《开发之路(设计模式八:外观模式)》

是不是感到很轻松了,我只需要“放入”我想看的电影,剩下一切程序自动化完成。

外观模式定义:提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。

再来一张图来理解外观
《开发之路(设计模式八:外观模式)》

这里就引出一个“最少知识”原则
最少知识原则告诉我们要减少对象之间的交互,只留下几个“密友”,当你在设计一个系统的时候,,不管是任何对象,都要注意它所交互的类有哪些,并注意它和这些类是如何交互的。
这个原则希望我们设计中,不要让太多的类耦合在一起,免得修改系统一部分,就会影响到其他部分。如果许多类之间相互依赖,那么系统就会变得牵一发而动全身了也因为要复杂而不容易被人理解。

要点:
1、当需要简化并统一一个很大的接口或者一群复杂的接口时,使用外观。
2、外观将客户从一个复杂的子系统中解耦出来。
3、实现一个外观,将子系统组合委托给外观,然后外观将工作让子系统执行。

感谢你看到这里,外观模式到这里就结束了,本人文笔随便,若有不足或错误之处望给予指点,90度弯腰~~~很快我会发布下一个设计模式的内容,生命不息,编程不止!

参考书籍:《Head First 设计模式》
    原文作者:设计模式
    原文地址: https://segmentfault.com/a/1190000011229235
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞