0.参考资料



1.概述

  • 为子系统中的一组接口提供一个一致(稳定)的界面, Facade模式定义了一个高层接口,这个接口使得这一子系 统更加容易使用(复用)。 —《设计模式》GoF
  • 外观模式(Facade),也叫“门面模式:外观模式通过定义一个一致的接口,用以屏蔽内部子系统的细节,使得调用端 只需跟这个接口发生调用,而无需关心这个子系统的内部细节

1.1动机

  1. - 组件的客户端和组件中各种复杂的子系统有了

过多的耦合,随着外部客户程序和各子系统的演化,这种过多的耦 合面临很多变化的挑战。

1.2结构

  1. - 其中子系统中, 各个组件的依赖由组件自行解决
  2. - ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1629870761699-2f9455c6-508d-4c78-8295-a741b0161be9.png#clientId=udfdf97de-3913-4&from=paste&height=217&id=u8fc7400f&margin=%5Bobject%20Object%5D&name=image.png&originHeight=289&originWidth=744&originalType=binary&ratio=1&size=62466&status=done&style=none&taskId=ua0b82d14-1185-4f8d-9a48-325979ef571&width=558)
  3. - ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1629876135502-18cd7843-0211-4f70-9649-a043ba2daffd.png#clientId=udfdf97de-3913-4&from=paste&id=u9bf3cd05&margin=%5Bobject%20Object%5D&name=image.png&originHeight=159&originWidth=426&originalType=binary&ratio=1&size=6325&status=done&style=none&taskId=u5748b11b-06d8-4b01-a4aa-3ffa240080f)

1.3说明

  1. 1. 外观类(Facade): 为调用端提供统一的调用接口, 外观类知道哪些子系统负责处理请求,从而将调用端的请求代理给适当

子系统对象

  1. 1. 调用者(Client): 外观接口的调用者
  2. 1. 子系统的集合:指模块或者子系统,处理Facade 对象指派的任务,他是功能的实际提供者

2.要点总结

宏观架构

  1. 1. 从客户程序的角度来看,Facade模式简化了整个组件系统的接口

; 对于组件内部与外部客户程序来说,达到了一种”解耦”的效 果 — 内部子系统的任何变化不会影响到Facade接口的变化。

  1. 1. Facade设计模式更注重从架构的层次去看整个系统, 而不是单个

类的层次。Facade很多时候更是一种架构设计模式。

  1. 1. Facade设计模式并非一个集装箱,可以任意地放进任何多个对象。

Facade模式中组件的内部应该是“相互耦合关系比较大的一系列 组件”, 而不是一个随意的功能集合。

微观代码

  1. 1. 外观模式对外屏蔽了子系统的细节,因此外观模式降低了客户端对子系统使用的复

杂性

  1. 1. 外观模式对客户端与子系统的耦合关系,让子系统内部的模块更易维护和扩展
  2. 1. 通过合理的使用外观模式,可以帮我们更好的划分访问的层次
  3. 1. 当系统需要进行分层设计时,可以考虑使用Facade模式
  4. 1. 在维护一个遗留的大型系统时,可能这个系统已经变得非常难以维护和扩展,此时

可以考虑为新系统开发一个Facade类,来提供遗留系统的比较清晰简单的接口, 让新系统与Facade类交互,提高复用性

  1. 1. 不能过多的或者不合理的使用外观模式,要以让系统有层次,利于维护为目的选择 外观模式/直接调用模块

3.案例

需求

  1. - 组建一个家庭影院:
  2. - DVD播放器、投影仪、自动屏幕、环绕立体声、爆米花机,要求完成使用家庭影院的
  3. - 功能,其过程为:
  4. - 直接用遥控器:统筹各设备开关
  5. - 开爆米花机
  6. - 放下屏幕
  7. - 开投影仪
  8. - 开音响
  9. - DVD,选dvd
  10. - 去拿爆米花
  11. - 调暗灯光
  12. - 播放
  13. - 观影结束后,关闭各种设备

分析

  1. - ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1629875972404-c033a4dc-f0d4-4aa2-b396-dbaf037341d0.png#clientId=udfdf97de-3913-4&from=paste&height=257&id=udd70a122&margin=%5Bobject%20Object%5D&name=image.png&originHeight=514&originWidth=1113&originalType=binary&ratio=1&size=138921&status=done&style=none&taskId=u9a4799af-14e3-4dec-ad39-c74b07fc774&width=556.5)
  2. - ClientTest main方法中,创建各个子系统的对象,并直接去调用子系统(对象) 相关方法,会造成调用过程混乱,没有清晰的过程
  3. -

不利于在ClientTest 中,去维护对子系统的操作

  1. - **解决思路**:
  2. - 定义一个高层接口,给子系统中的一组接口提供一个一致的界面(比

如在高层接口提供四个方法 ready, play, pause, end ),用来访问子系统中的 一群接口

  1. - 即通过定义一个一致的接口(界面类),用以屏蔽内部子系统的细节,

使得调用端只需跟这个接口发生调用,而无需关心这个子系统的内部细节 => 外观 模式

4.使用模式

改进引入案例

  1. - 外观模式可以理解为转换一群接口,客户只

要调用一个接口,而不用调用多个接口才能 达到目的。比如:在pc上安装软件的时候经 常有一键安装选项(省去选择安装目录、安 装的组件等等),还有就是手机的重启功能 (把关机和启动合为一个操作)。

  1. -

外观模式就是解决多个复杂接口带来的使用 困难,起到简化用户操作的作用

  1. - 分析:![](https://cdn.nlark.com/yuque/0/2021/png/12524106/1629876339696-067e26a4-7176-4e03-ba16-948f1e8cc5a9.png#from=url&height=224&id=KwjXm&margin=%5Bobject%20Object%5D&originHeight=448&originWidth=606&originalType=binary&ratio=1&status=done&style=none&width=303)
  2. - 类图: ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1629876795758-bfc6e293-b1ba-4868-b7ee-9322cb4d2179.png#clientId=udfdf97de-3913-4&from=paste&height=141&id=ub4008a6f&margin=%5Bobject%20Object%5D&name=image.png&originHeight=281&originWidth=526&originalType=binary&ratio=1&size=28036&status=done&style=none&taskId=u7a95ef3f-4df4-4736-9fcd-c5ee11f2631&width=263)

新案例

  1. - 模拟去网吧玩电脑和回家

方案

  1. - 使用外观模式,分为 三部分 _(准备 _- _玩电脑 _- _收尾)_
  2. - 每个部分的具体操作则调用具体的子部件组合而成

类图

  1. - ![{3$U[_4~P)1`A6YO5AD1_R3.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1629876830488-7160619f-5db9-49e6-b81a-ea4b77a67b48.png#clientId=udfdf97de-3913-4&from=paste&height=367&id=u3ccc5243&margin=%5Bobject%20Object%5D&name=%7B3%24U%5B_4~P%291%60A6YO5AD1_R3.png&originHeight=733&originWidth=672&originalType=binary&ratio=1&size=39104&status=done&style=none&taskId=u7be20acb-d66a-42ae-8efa-2abb8bdecf2&width=336)
  2. - 可以看到和客户端Client直接耦合的只有外观类, 而其三大步中的具体实现则有各种组件(互相调用)完成. 组件之间的依赖由组件自身解决.

代码

  1. - 门面类
  1. /**
  2. * @description: 网吧(准备 - 玩电脑 - 收尾)的外观类
  3. * 本案例简单模拟组件之间的依赖: 归属于User的RMB和SFZ
  4. */
  5. public class InternetFacade {
  6. User user;
  7. WBManager manager;
  8. Computer computer;
  9. {
  10. user = new User("小明");
  11. manager = WBManager.getInstance();
  12. computer = Computer.getInstance();
  13. }
  14. // 玩电脑的准备工作
  15. public void ready(){
  16. user.gotoWb();
  17. manager.showComputer();
  18. manager.register(user);
  19. computer.on();
  20. }
  21. // 玩电脑中
  22. public void play(){
  23. computer.play();
  24. }
  25. // 中途加钱
  26. public void addTime(){
  27. computer.stop();
  28. manager.addTime(user);
  29. }
  30. // 下机走人
  31. public void end(){
  32. computer.shutdown();
  33. manager.getOut(user);
  34. user.backHome();
  35. }
  36. }
  1. - 组件类
  1. @Getter
  2. public class User {
  3. String name;
  4. public User(String name){
  5. this. name = name;
  6. }
  7. private SFZ mySfz = new SFZ();
  8. private RMB myRmb = new RMB();
  9. public void showSfz(){
  10. mySfz.show(name);
  11. }
  12. public void payRMB(){
  13. myRmb.pay(name);
  14. }
  15. public void addRMB(){
  16. myRmb.add(name);
  17. }
  18. public void gotoWb(){
  19. System.out.println("从家到了网吧");
  20. }
  21. public void backHome(){
  22. System.out.println("回到了家");
  23. }
  24. }
  25. // 身份证
  26. public class SFZ {
  27. public void show(String name){
  28. System.out.println("\t" + name + " SFZ: 展示了身份证");
  29. }
  30. }
  31. // 钱
  32. public class RMB {
  33. // 各种行为, 案例中就不具体调用...
  34. public void pay(String name){
  35. System.out.println("\t" + name + " RMB: 付了钱");
  36. }
  37. public void add(String name){
  38. System.out.println("\t" + name + " RMB: 收了钱");
  39. }
  40. }
  41. // 网管
  42. public class WBManager {
  43. private static WBManager instance = new WBManager();
  44. public static WBManager getInstance() {
  45. return instance;
  46. }
  47. public void register(User user) {
  48. System.out.println("前台上号中...(依赖 SFZ类 和 RMB类");
  49. System.out.println("\t展示身份证中...");
  50. user.showSfz();
  51. System.out.println("\t付钱中...");
  52. user.payRMB();
  53. System.out.println("前台上号成功");
  54. }
  55. public void showComputer(){
  56. System.out.println("网管介绍网吧座位分区中");
  57. }
  58. public void addTime(User user) {
  59. System.out.println("前台加钱中...(依赖 SFZ类 和 RMB类");
  60. System.out.println("\t展示身份证中...");
  61. user.showSfz();
  62. System.out.println("\t付钱中...");
  63. user.payRMB();
  64. System.out.println("前台加钱成功");
  65. }
  66. public void getOut(User user) {
  67. System.out.println("下机中...(依赖 SFZ类 和 RMB类");
  68. System.out.println("\t展示身份证中...");
  69. user.showSfz();
  70. System.out.println("\t退钱中...");
  71. user.addRMB();
  72. }
  73. }
  74. // 上的机子
  75. public class Computer {
  76. private static Computer instance = new Computer();
  77. public static Computer getInstance() {
  78. return instance;
  79. }
  80. public void on (){
  81. System.out.println("电脑开机");
  82. }
  83. public void play(){
  84. System.out.println("电脑游戏中");
  85. }
  86. public void stop(){
  87. System.out.println("电脑挂起");
  88. }
  89. public void shutdown(){
  90. System.out.println("电脑关机");
  91. }
  92. }
  1. - 测试
  1. public class Client {
  2. public static void main(String[] args) {
  3. InternetFacade internetFacade = new InternetFacade();
  4. // 玩电脑的前摇
  5. internetFacade.ready();
  6. // 玩电脑中
  7. internetFacade.play();
  8. // 可能的插曲: 余额不足准备加钱
  9. if (true){
  10. internetFacade.addTime();
  11. }
  12. // 后摇
  13. internetFacade.end();
  14. }
  15. }
  1. 从家到了网吧
  2. 网管介绍网吧座位分区中
  3. 前台上号中...(依赖 SFZ RMB
  4. 展示身份证中...
  5. 小明 SFZ: 展示了身份证
  6. 付钱中...
  7. 小明 RMB: 付了钱
  8. 前台上号成功
  9. 电脑开机
  10. 电脑游戏中
  11. 电脑挂起
  12. 前台加钱中...(依赖 SFZ RMB
  13. 展示身份证中...
  14. 小明 SFZ: 展示了身份证
  15. 付钱中...
  16. 小明 RMB: 付了钱
  17. 前台加钱成功
  18. 电脑关机
  19. 下机中...(依赖 SFZ RMB
  20. 展示身份证中...
  21. 小明 SFZ: 展示了身份证
  22. 退钱中...
  23. 小明 RMB: 收了钱
  24. 回到了家

5.经典使用


5.1MyBatis中Configuration.newMetaObject(Object obj) : MetaObject

代码

  1. - ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1629878358031-b44c105a-e58a-4e38-851a-be179bb52a06.png#clientId=udfdf97de-3913-4&from=paste&id=ua3ba2c3a&margin=%5Bobject%20Object%5D&name=image.png&originHeight=390&originWidth=981&originalType=binary&ratio=1&size=643442&status=done&style=none&taskId=ucabd19d2-7fc0-498a-9a35-bc4f8d20e74)

类图

  1. - ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1629878438999-d6e7d765-69ec-4549-8685-fba4f13601b6.png#clientId=udfdf97de-3913-4&from=paste&id=ub41512b4&margin=%5Bobject%20Object%5D&name=image.png&originHeight=485&originWidth=770&originalType=binary&ratio=1&size=173921&status=done&style=none&taskId=ue600d7e7-cfa8-46de-a0ef-9f22cf9cc92)