• 尚硅谷设计模式学习(十一)外观模式


    以家庭影院引入外观者模式

    组建一个家庭影院:

    DVD播放器、投影仪、自动屏幕、环绕立体声、爆米花机,要求完成使用家庭影院的功能,其过程为:

    • 直接用遥控器:统筹各设备开关
    • 开爆米花机
    • 放下屏幕
    • 开投影仪
    • 开音响
    • 开DVD,选dvd
    • 去拿爆米花
    • 调暗灯光
    • 播放
    • 观影结束后,关闭各种设备

    传统思路

    直接让客户端依赖(使用) 所有的设备对象,这样的设计会使得客户端比较混乱,一时间难以兼顾维护多个设备。

    问题分析

    在 Client 的 main 方法中,创建各个子系统的对象,并直接去调用子系统(对象)相关方法,会造成调用过程混乱,没有清晰的过程

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

    解决思路

    给设备和客户端之间 “加一层”,让中间这一层去管理设备,客户端只要和中间这层对接即可。

    一、外观模式

    外观模式通过定义一个一致的接口,用以屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关心这个子系统的内部细节。

    例如:

    • 在 pc 上安装软件的时候经常有一键安装选项(省去选择安装目录、安装的组件等等)
    • 手机的重启功能(把关机和启动合为一个操作)。

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

    原理类图

    • 外观类(Facade):为调用端提供统一的调用接口,外观类知道哪些子系统负责处理请求,从而将调用端的请求代理给适当子系统对象。
    • 调用者(Client):外观接口的调用者
    • 子系统的集合:指模块或者子系统,处理 Facade 对象指派的任务,它是功能的实际提供者。

    2、代码实现

    三个子系统

    1. //DVD播放器
    2. public class DVDPlayer {
    3. private static DVDPlayer instance = new DVDPlayer();
    4. public static DVDPlayer getInstance(){
    5. return instance;
    6. }
    7. //DVD的操作方法;
    8. public void open(){
    9. System.out.println("DVD开机");
    10. }
    11. public void close(){
    12. System.out.println("DVD关机");
    13. }
    14. public void pause(){
    15. System.out.println("DVD暂停");
    16. }
    17. public void option(){
    18. System.out.println("DVD使用操作");
    19. }
    20. }
    1. //灯光
    2. public class Lamplight {
    3. private static Lamplight instance = new Lamplight();
    4. public static Lamplight getInstance(){
    5. return instance;
    6. }
    7. //管理灯光;
    8. public void openLamp(){
    9. System.out.println("开灯");
    10. }
    11. public void closeLamp(){
    12. System.out.println("关灯");
    13. }
    14. }
    1. //爆米花设备
    2. public class PopCorn {
    3. private static PopCorn instance = new PopCorn();
    4. public static PopCorn getInstance(){
    5. return instance;
    6. }
    7. //爆米花设备操作;
    8. public void openPop(){
    9. System.out.println("爆米花设备开机");
    10. }
    11. public void closePop(){
    12. System.out.println("爆米花设备关机");
    13. }
    14. public void pop(){
    15. System.out.println("取爆米花");
    16. }
    17. }

    外观类

    1. //外观类
    2. public class HomeTheaterFacade {
    3. // 管理多个设备
    4. private DVDPlayer dvdPlayer;
    5. private PopCorn popCorn;
    6. private Lamplight screen;
    7. public HomeTheaterFacade(){
    8. dvdPlayer = DVDPlayer.getInstance();
    9. popCorn = PopCorn.getInstance();
    10. screen = Lamplight.getInstance();
    11. }
    12. //观影前准备
    13. public void ready(){
    14. popCorn.openPop();
    15. popCorn.pop();
    16. screen.openLamp();
    17. }
    18. //观影中管理
    19. public void play(){
    20. dvdPlayer.open();
    21. }
    22. //暂停观影
    23. public void pause(){
    24. dvdPlayer.pause();
    25. }
    26. //结束观影
    27. public void end(){
    28. dvdPlayer.close();
    29. popCorn.closePop();
    30. screen.closeLamp();
    31. }
    32. }

     使用测试

    1. public class Client {
    2. public static void main(String[] args) {
    3. HomeTheaterFacade homeTheaterFacade = new HomeTheaterFacade();
    4. homeTheaterFacade.ready();
    5. homeTheaterFacade.play();
    6. homeTheaterFacade.pause();
    7. homeTheaterFacade.end();
    8. }
    9. }

    结果

    爆米花设备开机
    取爆米花
    开灯
    DVD开机
    DVD暂停
    DVD关机
    爆米花设备关机
    关灯

     二、外观模式在  MyBatis  框架应用的源码分析

    用户只需要调用Configuration的newMetaObject(Object object)方法就可以得到一个MetaObject对象,而具体的对象是怎么生成与用户无关,下面我们可以看一下Configuration的部分源码分析。

    1. public class Configuration {
    2. public MetaObject newMetaObject(Object object) {
    3. return MetaObject.forObject(object, this.objectFactory, this.objectWrapperFactory, this.reflectorFactory);
    4. }
    5. //......
    6. }

    下面看看Configuration的构造器,它里面创建了这三个实例。

    1. public Configuration() {
    2. this.reflectorFactory = new DefaultReflectorFactory();
    3. this.objectFactory = new DefaultObjectFactory();
    4. this.objectWrapperFactory = new DefaultObjectWrapperFactory();
    5. //......
    6. }

    进入MetaObject类的forObject方法 

    1. public static MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
    2. return object == null ? SystemMetaObject.NULL_META_OBJECT : new MetaObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
    3. }

    它里面聚合了objectFactory,objectWrapperFactory,reflectorFactory

    objectFactory,objectWrapperFactory,reflectorFactory可以看作三个子系统

    1. public class MetaObject {
    2. private final Object originalObject;
    3. private final ObjectWrapper objectWrapper;
    4. private final ObjectFactory objectFactory;
    5. private final ObjectWrapperFactory objectWrapperFactory;
    6. private final ReflectorFactory reflectorFactory;
    7. private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
    8. this.originalObject = object;
    9. this.objectFactory = objectFactory;
    10. this.objectWrapperFactory = objectWrapperFactory;
    11. this.reflectorFactory = reflectorFactory;
    12. if (object instanceof ObjectWrapper) {
    13. this.objectWrapper = (ObjectWrapper)object;
    14. } else if (objectWrapperFactory.hasWrapperFor(object)) {
    15. this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);
    16. } else if (object instanceof Map) {
    17. this.objectWrapper = new MapWrapper(this, (Map)object);
    18. } else if (object instanceof Collection) {
    19. this.objectWrapper = new CollectionWrapper(this, (Collection)object);
    20. } else {
    21. this.objectWrapper = new BeanWrapper(this, object);
    22. }
    23. }
    24. //......
    25. }

    三、外观模式的注意事项和细节

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

    2)外观模式让客户端与子系统解耦,让子系统内部的模块更易维护和扩展

    3)通过合理的使用外观模式,可以帮我们更好的划分访问的层次

    4)当系统需要进行分层设计时,可以考虑使用  Facade  模式

    5)在维护一个遗留的大型系统时,可能这个系统已经变得非常难以维护和扩展,此时可以考虑为新系统开发一个 Facade  类,来提供遗留系统的比较清晰简单的接口,让新系统与  Facade  类交互,提高复用性

    6)不能过多的或者不合理的使用外观模式,使用外观模式好,还是直接调用模块好。要以让系统有层次,利于维护为目的。

  • 相关阅读:
    ElasticSearch总结
    Educational Codeforces Round 135 (A-D题)
    Tomcat老是卡住,关不掉怎么办?
    关于燃气智慧场站标准化建设的思考
    Word处理控件Aspose.Words功能演示:使用 Python 创建 MS Word 文档
    Spring
    【题解 && 单调队列优化dp】 简单的加法乘法计算题
    elasticsearch 8.5.3问题记录
    Android编译C/C++代码,编译出的so文件给别的项目用,CMakeLists.txt编译,请放弃Android.mk!
    CAN总线入门必看:如何运用虚拟驱动编写测试脚本
  • 原文地址:https://blog.csdn.net/qq_51409098/article/details/126907981