一、概述
外观模式提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。
二、解决问题
在上一讲中,我们学习了适配器模式,它是用来转换一个接口的,而外观模式可以理解为转换一群接口,客户只要调用一个接口,而不用调用多个接口就可以达到目的。想想现实生活中例子,我们在pc上安装软件的时候经常有默认安装或者是一键安装选项(省去选择安装目录、安装的组件等等),还有就是手机的重启功能(把关机和启动合为一个操作)。总之,外观模式就是解决多个复杂接口带来的使用困难,起到简化用户操作的作用。
三、结构类图
四、成员角色
客户(Client):直接调用外观接口就可以使用子系统了。
外观(Facade):封装子系统的一个或者多个接口,并提供一个统一的接口给客户端调用。
五、应用实例
最近几年一直比较流行智能家居,在我看来就是很多家电连了网,我们可以在手机上操作这些家电,给我们带来了很大的便利。下面我们假设这样一种场景,我们晚上回到家需要开灯、开空调、关窗、拉窗帘、放音乐,完成这五个动作我们才可以舒舒服服地躺在沙发上,缓解一天工作的辛劳。如果每天要进行这样的操作就太麻烦了,现在我们的智能家居来了,他给我们提供了一个手机app,只要将近到家的时候按下一个按钮,各种家电就会自动工作,营造一个舒适的家庭环境等着我们回来。来看看我们是怎么做到的。
第一步、创建各种电器
package facade.pattern; //电灯 public class Light { public void on(){ System.out.println("电灯打开了"); } public void off(){ System.out.println("电灯关了"); } }
package facade.pattern; //空调 public class Aircondition { public void on(){ System.out.println("空调打开了"); } public void off(){ System.out.println("空调关了"); } }
package facade.pattern; //窗 public class Window { public void on(){ System.out.println("窗打开了"); } public void off(){ System.out.println("窗关了"); } }
package facade.pattern; //窗帘 public class Curtain { public void on(){ System.out.println("窗帘打开了"); } public void off(){ System.out.println("窗帘关了"); } }
package facade.pattern; //mp3播放器 public class Mp3player { public void on(){ System.out.println("mp3播放器打开了"); } public void off(){ System.out.println("mp3播放器关了"); } }
第二步、创建手机app
package facade.pattern; //手机app public class App { private Light light; private Aircondition aircondition; private Window window; private Curtain curtain; private Mp3player mp3; //实例化时初始化电器 public App(Light light, Aircondition aircondition, Window window, Curtain curtain, Mp3player mp3) { super(); this.light = light; this.aircondition = aircondition; this.window = window; this.curtain = curtain; this.mp3 = mp3; } //设置在家模式,开启需要的家电 public void atHome(){ light.on(); aircondition.on(); window.off(); curtain.off(); mp3.on(); } }
第三步、测试手机app
package facade.pattern; public class AppTest { public static void main(String[] args){ Light light = new Light(); Aircondition aircondition = new Aircondition(); Window window = new Window(); Curtain curtain = new Curtain(); Mp3player mp3 = new Mp3player(); App app = new App(light,aircondition,window,curtain,mp3); //以上代码客户可以不用知道 System.out.println("---用手机设置智能家居,设置为在家模式---"); //客户一键操作设置在家模式 app.atHome(); } }
运行结果:
六、优点和缺点
1、优点
(1)、外观不只是简化了接口,也将客户从组建的子系统中解耦。客户只需关注外观提供的接口,而不需要关心外观所封装的子系统的类。
(2)、提供简化的接口的同时,依然将系统完整的功能暴露出来,以供需要的人使用。
七、使用场景
1、当需要简化并统一一个很大的接口或者简化一群复杂的接口时使用外观。
2、希望客户从一个复杂的子系统中解耦。
八、在外观模式中可以使用的设计原则--最少知识原则
最少知识原则定义:只和你的密友交谈。
最少知识原则设计方针是在就任何对象而言,在该对象的方法内,我们只应该调用属于以下范围的方法:
1、该对象本身
package facade.pattern; public class LightTest { public void on(){ System.out.println("电灯打开了"); } public void off(){ System.out.println("电灯关了"); } public void test(){ //在方法中调用本类中的其他方法 on(); } }
2、被当做方法的参数而传递进来的对象(外观模式手机app的例子)
//手机app public class App { //外出模式 public void goOut(Light light, Aircondition aircondition, Window window, Curtain curtain, Mp3player mp3){ light.off(); aircondition.off(); window.on(); curtain.on(); mp3.off(); } }
3、此方法所创建或实例化的任何对象
//手机app public class App { private Light light; private Aircondition aircondition; private Window window; private Curtain curtain; private Mp3player mp3; public App(){ } public Mp3player getMp3() { return mp3; } public void setMp3(Mp3player mp3) { this.mp3 = mp3; } public void startMp3(){ mp3.on(); } }
//不建议如下使用
public void startMp3player(){ Mp3player mp3 = new App().getMp3(); mp3.on(); }
应该改为
public void startMp3player(){ new App().startMp3(); }
4、对象的任何组件
//手机app public class App { private Light light; private Aircondition aircondition; private Window window; private Curtain curtain; private Mp3player mp3; public App(){ } //在该方法中,可以使用组件mp3的任何方法 public void startMp3(){ mp3.on(); } }