外观模式
影院管理项目
我们先举现实中一个家庭影院的例子
DVD播放器、投影仪、自动屏幕、环绕立体声、爆米花机,要求完成使用家庭影院的功能,其过程为:
直接用遥控器:统筹各设备开关
• 开爆米花机
• 放下屏幕
• 开投影仪
• 开音响
• 开DVD,选dvd
• 去拿爆米花
• 调暗灯光
• 播放
• 观影结束后,关闭各种设备
传统方式解决影院管理
传统方式解决影院管理问题分析
-
在ClientTest 的main方法中,创建各个子系统的对象,并直接去调用子系统(对象)相关方法,会造成调用过程混乱,没有清晰的过程
-
不利于在 ClientTest 中,去维护对子系统的操作
-
解决思路:定义一个高层接口,给子系统中的一组接口提供一个一致的界面(比如在高层接口提供四个方法 ready,play,pause,end),用来访问子系统中的一群接口
-
也就是说就是通过定义一个一致的接口(界面类),用以屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关心这个子系统的内部细节 => 外观模式
外观模式基本介绍
-
外观模式(Facade),也叫“过程模式”:外观模式为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用
-
外观模式通过定义一个一致的接口,用以屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关心这个子系统的内部细节
外观(Facade)模式是“迪米特法则”的典型应用,它有以下主要优点
-
降低了子系统与客户端之间的耦合度,使得子系统的变化不会影响调用它的客户类
-
对客户屏蔽了子系统组件,减少了客户处理的对象数目,并使得子系统使用起来更加容易
-
降低了大型软件系统中的编译依赖性,简化了系统在不同平台之间的移植过程,因为编译一个子系统不会影响其他的子系统,也不会影响外观对象
外观(Facade)模式的主要缺点如下
-
不能很好地限制客户使用子系统类
-
增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”
外观模式的结构与实现
外观(Facade)模式的结构比较简单,主要是定义了一个高层接口。它包含了对各个子系统的引用,客户端可以通过它访问各个子系统的功能。现在来分析其基本结构和实现方法。
1. 模式的结构
外观(Facade)模式包含以下主要角色
-
外观(Facade)角色:为多个子系统对外提供一个共同的接口
-
子系统(Sub System)角色:实现系统的部分功能,客户可以通过外观角色访问它
-
客户(Client)角色:通过一个外观角色访问各个子系统的功能
2. 模式的实现
外观模式的实现代码如下:
1 package facade; 2 3 public class FacadePattern { 4 5 public static void main(String[] args) { 6 Facade f=new Facade(); 7 f.method(); 8 } 9 } 10 11 12 // 外观角色 13 class Facade { 14 15 private SubSystem01 obj1=new SubSystem01(); 16 private SubSystem02 obj2=new SubSystem02(); 17 private SubSystem03 obj3=new SubSystem03(); 18 19 public void method() { 20 obj1.method1(); 21 obj2.method2(); 22 obj3.method3(); 23 } 24 } 25 26 27 // 子系统角色 28 class SubSystem01 { 29 30 public void method1() { 31 System.out.println("子系统01的method1()被调用!"); 32 } 33 } 34 35 36 // 子系统角色 37 class SubSystem02 { 38 public void method2() 39 { 40 System.out.println("子系统02的method2()被调用!"); 41 } 42 } 43 44 45 // 子系统角色 46 class SubSystem03 { 47 48 public void method3() { 49 System.out.println("子系统03的method3()被调用!"); 50 } 51 }
程序运行结果如下
-
子系统01的method1()被调用!
-
子系统02的method2()被调用!
-
子系统03的method3()被调用!
外观模式解决影院管理
传统方式解决影院管理说明
-
外观模式可以理解为转换一群接口,客户只要调用一个接口,而不用调用多个接口才能达到目的。比如:在pc上安装软件的时候经常有一键安装选项(省去选择安装目录、安装的组件等等),还有就是手机的重启功能(把关机和启动合为一个操作)
-
外观模式就是解决多个复杂接口带来的使用困难,起到简化用户操作的作用
-
示意图说明
外观模式应用实例
-
应用实例要求 使用外观模式来完成家庭影院项目
-
思路分析和图解(类图)
-
代码实现
1 package com.atguigu.facade; 2 3 public class Client { 4 5 public static void main(String[] args) { 6 7 //这里直接调用。。 很麻烦 8 HomeTheaterFacade homeTheaterFacade = new HomeTheaterFacade(); 9 homeTheaterFacade.ready(); 10 homeTheaterFacade.play(); 11 homeTheaterFacade.end(); 12 } 13 }
1 package com.atguigu.facade; 2 3 public class DVDPlayer { 4 5 // 使用单例模式, 使用饿汉式 6 private static DVDPlayer instance = new DVDPlayer(); 7 8 public static DVDPlayer getInstanc() { 9 return instance; 10 } 11 12 public void on() { 13 System.out.println(" dvd on "); 14 } 15 16 public void off() { 17 System.out.println(" dvd off "); 18 } 19 20 public void play() { 21 System.out.println(" dvd is playing "); 22 } 23 24 //.... 25 public void pause() { 26 System.out.println(" dvd pause .."); 27 } 28 }
package com.atguigu.facade; public class HomeTheaterFacade { // 定义各个子系统对象 private TheaterLight theaterLight; private Popcorn popcorn; private Stereo stereo; private Projector projector; private Screen screen; private DVDPlayer dVDPlayer; // 构造器 public HomeTheaterFacade() { super(); this.theaterLight = TheaterLight.getInstance(); this.popcorn = Popcorn.getInstance(); this.stereo = Stereo.getInstance(); this.projector = Projector.getInstance(); this.screen = Screen.getInstance(); this.dVDPlayer = DVDPlayer.getInstanc(); } // 操作分成 4 步 public void ready() { popcorn.on(); popcorn.pop(); screen.down(); projector.on(); stereo.on(); dVDPlayer.on(); theaterLight.dim(); } public void play() { dVDPlayer.play(); } public void pause() { dVDPlayer.pause(); } public void end() { popcorn.off(); theaterLight.bright(); screen.up(); projector.off(); stereo.off(); dVDPlayer.off(); } }
1 package com.atguigu.facade; 2 3 public class Popcorn { 4 5 private static Popcorn instance = new Popcorn(); 6 7 public static Popcorn getInstance() { 8 return instance; 9 } 10 11 public void on() { 12 System.out.println(" popcorn on "); 13 } 14 15 public void off() { 16 System.out.println(" popcorn off "); 17 } 18 19 public void pop() { 20 System.out.println(" popcorn is poping "); 21 } 22 }
等等子系统的类。。。
外观模式的应用场景
通常在以下情况下可以考虑使用外观模式
-
对分层结构系统构建时,使用外观模式定义子系统中每层的入口点可以简化子系统之间的依赖关系
-
当一个复杂系统的子系统很多时,外观模式可以为系统设计一个简单的接口供外界访问
-
当客户端与多个子系统之间存在很大的联系时,引入外观模式可将它们分离,从而提高子系统的独立性和可移植性
外观模式在MyBatis框架应用的源码分析
-
MyBatis 中的 Configuration 去创建 MetaObject 对象使用到外观模式
-
代码分析
外观模式的注意事项和细节
-
外观模式对外屏蔽了子系统的细节,因此外观模式降低了客户端对子系统使用的复杂性
-
外观模式对客户端与子系统的耦合关系 - 解耦,让子系统内部的模块更易维护和扩展
-
通过合理的使用外观模式,可以帮我们更好的划分访问的层次
-
当系统需要进行分层设计时,可以考虑使用Facade模式
-
在维护一个遗留的大型系统时,可能这个系统已经变得非常难以维护和扩展,此时可以考虑为新系统开发一个 Facade 类,来提供遗留系统的比较清晰简单的接口,让新系统与 Facade 类交互,提高复用性
-
不能过多的或者不合理的使用外观模式,使用外观模式好,还是直接调用模块好。要以让系统有层次,利于维护为目的
外观模式的扩展
在外观模式中,当增加或移除子系统时需要修改外观类,这违背了“开闭原则”。如果引入抽象外观类,则在一定程度上解决了该问题,其结构图如图 5 所示。
原文链接:https://blog.csdn.net/qq784515681/article/details/106415506