外观模式
- 定义:又叫门面模式,提供了一个统一的接口或外观类,用来访问子系统中的一群接口
- 特点:外观模式定义了一个高层接口,让子系统更容易使用
- 类型:结构型
- 适用场景:
- 子系统越来越复杂,增加外观模式提供简单调用接口
- 构建多层系统结构,利用外观对象作为每层的入口,简化层间调用
- 优点:
- 简化了调用过程,无需深入了解子系统,防止带来风险
- 减少系统依赖、松散耦合,外观模式松散了客户端与子系统的耦合关系,客户端不与子系统直接交流,客户端和外观对象直接交流,让子系统内部的模块更容易扩展和维护
- 更好的划分访问层次
- 符合迪米特原则,即最少知道原则
- 缺点:
- 增加子系统、扩展子系统行为容易引入风险
- 增加扩展子系统不符合开闭原则
- 相关设计模式:
- 外观模式和中介者模式:外观模式关注的是外界和子系统之间的交互,中介者模式关注的是子系统内部之间的交互
- 外观模式和单例模式:通常会把外观模式中的外观对象做成单例模式
- 外观模式和抽象工厂模式:外观类可以通过抽象工厂获取子系统的实例,这样子系统在内部可以对外观类进行屏蔽
coding
moduleA
/** * <p>模块A下的子系统A</p> */ public class SubSystemA { public void initSystem() { System.out.println("人工智能系统正在启动,请您稍等.... =====>" + this); for (int i = 0; i < 3; i++) { try { System.out.println("等待" + (i + 1) + "s"); Thread.sleep(1000); } catch (InterruptedException ex) { System.out.println(ex.getClass() + "," + ex.getMessage()); } } } @Override public String toString() { return "人工智能系统,模块A,子系统A,主要负责启动系统"; } } /** * <p>模块A下的子系统B</p> * * @author Appleyk * @version v0.1.1 * @blob https://blog.csdn.net/appleyk * @date Created on 上午 9:00 2018-11-12 */ public class SubSystemB { public void loadDatas() { System.out.println("人工智能系统已经启动,正在加载数据 =====>" + this); System.out.println("........."); System.out.println("数据已完成加载"); } @Override public String toString() { return "人工智能系统,模块A,子系统B,主要负责加载数据"; } } /** * <p>外观模式A == 聚合子模块A中的子系统A和B,降低客户端操作子模块A的复杂度</p> */ public class FacadeA { private SubSystemA subSystemA; private SubSystemB subSystemB; public FacadeA() { subSystemA = new SubSystemA(); subSystemB = new SubSystemB(); } /** * 将模块A的功能封装一下,松散客户端与A模块中的各个子系统间的耦合关系 * 让模块中的各个子系统更容易扩展和维护 */ public void initialize() { subSystemA.initSystem(); subSystemB.loadDatas(); } }
moduleB
/** * <p>模块B下的子系统C</p> */ public class SubSystemC { public void sayHello() { System.out.println("欢迎进入Appleyk's 基于电影知识图谱的人工智能系统 =====>" + this); } @Override public String toString() { return "人工智能系统,模块B,子系统C,欢迎界面"; } } /** * <p>模块B下的子系统D</p> * * @author Appleyk * @version v0.1.1 * @blob https://blog.csdn.net/appleyk * @date Created on 上午 9:00 2018-11-12 */ public class SubSystemD { public void working() { System.out.println("亲爱的用户,以下是你18年截止到今天看的电影的大数据统计结果:=====>" + this); System.out.println("《《《《 ========== 》》》》》\n爱情动作片:10次\n" + "恐怖片:1次\n" + "喜剧片:6次\n" + "总花费:680.5元\n" + "《《《《 ========== 》》》》》"); } @Override public String toString() { return "人工智能系统,模块B,子系统D,开始工作"; } } /** * <p>外观模式B == 聚合子模块B中的子系统C和D,降低客户端操作子模块B的复杂度</p> */ public class FacadeB { private SubSystemC subSystemC; private SubSystemD subSystemD; public FacadeB() { subSystemC = new SubSystemC(); subSystemD = new SubSystemD(); } /** * 将模块A的功能封装一下,松散客户端与A模块中的各个子系统间的耦合关系 * 让模块中的各个子系统更容易扩展和维护 */ public void work() { subSystemC.sayHello(); subSystemD.working(); } }
外观模式AB
/** * <p>外观模式AB == 聚合子模块A和子模块B的功能,降低客户端使用模块A和模块B的复杂度</p> */ public class FacadeAB { private FacadeA facadeA; private FacadeB facadeB; public FacadeAB() { facadeA = new FacadeA(); facadeB = new FacadeB(); } public void startSystem() { facadeA.initialize(); facadeB.work(); } }
测试
/** * <p>外观模式测试</p> */ public class FacadeTest { public static void main(String[] args) { /** * 不使用外观模式,直接使用智能人工系统的模块A和模块B的功能 * 缺点:用户必须清楚模块中的各个子系统的工作流程,否则会导致系统的不正常工作 */ useModuleA(); useModuleB(); /** * 使用外观模式A和外观模式B * 缺点:依然不够简洁 */ useModuleAByFacadeA(); useModuleAByFacadeB(); /** * 使用外观模式AB * 优点:用户使用系统,相当的便捷,没有多余的废话 */ useSystemByFacadeAB(); /** * 通过以上的对比,应用层(FacadeTest )是不需要关心子系统,只要保证和外观类(FacadeAB )通信即可,具体里面初始化,加载数据等,应用层不需要关心 */ } /** * 不使用外观模式,直接使用模块A的功能 */ private static void useModuleA() { partition("直接使用模块A中的功能"); SubSystemA subSystemA = new SubSystemA(); SubSystemB subSystemB = new SubSystemB(); subSystemA.initSystem(); subSystemB.loadDatas(); } /** * 不使用外观模式,直接使用模块B的功能 */ private static void useModuleB() { partition("直接使用模块B中的功能"); SubSystemC subSystemC = new SubSystemC(); SubSystemD subSystemD = new SubSystemD(); subSystemC.sayHello(); subSystemD.working(); } /** * 使用外观模式A,对模块A中的功能进行"屏蔽" */ private static void useModuleAByFacadeA() { partition("使用外观模式A"); FacadeA facadeA = new FacadeA(); facadeA.initialize(); } /** * 使用外观模式B,对模块B中的功能进行"屏蔽" */ private static void useModuleAByFacadeB() { partition("使用外观模式A"); FacadeB facadeB = new FacadeB(); facadeB.work(); } /** * 使用外观模式AB,对外观模式A和B再进行一次"屏蔽" */ private static void useSystemByFacadeAB() { partition("使用外观模式AB"); FacadeAB facadeAB = new FacadeAB(); facadeAB.startSystem(); } private static void partition(String note) { System.out.println("============== 分割线【" + note + "】 =============="); } }
通过以上的对比,应用层(FacadeTest )是不需要关心子系统,只要保证和外观类(FacadeAB )通信即可,具体里面初始化,加载数据等,应用层不需要关心
UML
从UML中可以看出,它非常完美的支持了迪米特法则,我们这里使用的是实体外观类,但是如果要增加另一个子系统,就需要修改实体外观类,从这个角度分析并不符合开闭原则,我们可以再扩展一下,如果这个外观类使用抽象外观类的话,也就是实体外观类可以实现实体外观接口去操作。