十一、外观模式

news2024/11/15 9:52:16

文章目录

  • 1 基本介绍
  • 2 案例
    • 2.1 Person 类
    • 2.2 Computer 类
    • 2.3 Player 类
    • 2.4 TV 类
    • 2.5 StudyManager 类
    • 2.6 Client 类
    • 2.7 Client 类运行结果
    • 2.8 总结
  • 3 各角色之间的关系
    • 3.1 角色
      • 3.1.1 SubSystem ( 子系统 )
      • 3.1.2 Facade ( 窗口 )
      • 3.1.3 Client ( 客户端 )
    • 3.2 类图
  • 4 注意事项
  • 5 在源码中的使用
  • 6 优缺点
  • 7 适用场景
  • 8 总结


1 基本介绍

外观模式(Facade Pattern),又称 门面模式,是一种 结构型 设计模式,主要用于 为复杂的子系统提供一个简单、统一的接口,使得外部程序能够更加方便地与这些子系统交互,无需关心使用子系统的具体细节。

2 案例

本案例分为两个操作:

  • 学习:先播放指定类型的音乐,然后打开电脑的某个软件进行学习,学习完毕后关闭电脑的这个软件,并暂停播放音乐。
  • 娱乐:观看电影。

2.1 Person 类

public class Person { // 人
    public static void study(String name) {
        System.out.println("- [" + name + "]开始学习");
    }

    public static void watchFilm(String name) {
        System.out.println("- [" + name + "]开始看电影");
    }
}

2.2 Computer 类

public class Computer { // 电脑
    public static void openComputer() {
        System.out.println("- 打开电脑");
    }

    public static void openSoftware(String softwareName) {
        System.out.println("- 打开[" + softwareName +"]软件");
    }

    public static void closeSoftware(String softwareName) {
        System.out.println("- 关闭[" + softwareName +"]软件");
    }

    public static void closeComputer() {
        System.out.println("- 关闭电脑");
    }
}

2.3 Player 类

public class Player { // 播放器
    public static void playMusic(String musicType) {
        System.out.println("- 播放[" + musicType + "]音乐");
    }

    public static void pauseMusic(String musicType) {
        System.out.println("- 暂停播放[" + musicType + "]音乐");
    }
}

2.4 TV 类

public class TV { // 电视机
    public static void openTV() {
        System.out.println("- 打开电视");
    }

    public static void play(String fileName) {
        System.out.println("- 播放[" + fileName + "]电影");
    }

    public static void closeTV() {
        System.out.println("- 关闭电视");
    }
}

2.5 StudyManager 类

public class StudyManager { // 学习管理者
    public static void study(String name, String musicType, String softwareName) {
        System.out.println("以下是学习部分:");
        Player.playMusic(musicType);
        Computer.openComputer();
        Computer.openSoftware(softwareName);
        Person.study(name);
        Computer.closeSoftware(softwareName);
        Computer.closeComputer();
        Player.pauseMusic(musicType);
    }

    public static void relax(String name, String filmName) {
        System.out.println("以下是娱乐部分:");
        TV.openTV();
        TV.play(filmName);
        Person.watchFilm(name);
        TV.closeTV();
    }
}

2.6 Client 类

public class Client { // 客户端,测试 StudyManager 的 study() 和 relax()
    public static void main(String[] args) {
        StudyManager.study("sam", "爵士", "IDEA");
        StudyManager.relax("sam", "《加勒比海盗》");
    }
}

2.7 Client 类运行结果

以下是学习部分:
- 播放[爵士]音乐
- 打开电脑
- 打开[IDEA]软件
- [sam]开始学习
- 关闭[IDEA]软件
- 关闭电脑
- 暂停播放[爵士]音乐
以下是娱乐部分:
- 打开电视
- 播放[《加勒比海盗》]电影
- [sam]开始看电影
- 关闭电视

2.8 总结

本案例将 学习娱乐 所需的复杂操作封装到两个方法中,从而使客户端 Client 在执行这两个操作时只需要调用方法,无需知道具体的实现细节。如果没有封装,则需要将这些冗长的代码写在 Client 中重写一遍。

StudyManager 类就像一个 窗口 一样,窗口内部封装了调用 Person, Computer, Player, TV 类的逻辑,向外部只暴露两个接口来进行不同的操作。

3 各角色之间的关系

3.1 角色

3.1.1 SubSystem ( 子系统 )

该角色负责 完成自己的工作,无需知道 Facade 角色,也就是 SubSystem 角色不能调用 Facade 角色的方法。在本案例中,Person, Computer, Player, TV 类扮演本角色。

3.1.2 Facade ( 窗口 )

该角色负责 封装子系统的业务逻辑,向外部暴露简单的调用接口。在本案例中,StudyManager 类扮演本角色。

3.1.3 Client ( 客户端 )

该角色负责 调用 Facade 角色完成具体的业务。在本案例中,Client 类扮演本角色。

3.2 类图

alt text
注意:

  • 虽然图中的 SubSystem 之间没有相互调用的关系,但实际上它们是可以相互调用的。
  • Client 只能直接调用 Facade 中的方法,不能与 SubSystem 有直接关联。

4 注意事项

  • 合理划分访问层次:通过 合理地 使用外观模式,可以更好地划分系统的访问层次。外观类 作为 高层接口,为 客户端 提供了一个 清晰的 访问入口;而 子系统则作为 底层实现,负责 具体的业务逻辑处理
  • 避免过度使用:虽然外观模式有很多优点,但也不能 过度不合理 地使用。如果每个子系统都使用外观模式进行封装,可能会导致 系统结构过于复杂,反而增加了系统的维护难度。

5 在源码中的使用

java.lang.Class 类中的 forName() 方法中使用到了外观模式,它为 加载和初始化类 提供了一个简单的接口,隐藏了类加载和初始化的复杂过程,客户端只需调用该方法并传入类的全限定名,即可获取到该类的Class 对象,而无需关心该类是如何被加载和初始化的。

// java.lang.Class 类中有如下的 forName():
public static Class<?> forName(String className)
            throws ClassNotFoundException {
    Class<?> caller = Reflection.getCallerClass();
    return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}

// Reflection.getCallerClass() 如下所示:
public static native Class<?> getCallerClass(); // 是一个 native 方法,由其他语言实现

// Class 类的 forName0() 如下所示:
private static native Class<?> forName0(String name, boolean initialize,
                                        ClassLoader loader,
                                        Class<?> caller)
    throws ClassNotFoundException; // 是一个 native 方法,由其他语言实现

// ClassLoader.getClassLoader() 如下所示:
static ClassLoader getClassLoader(Class<?> caller) {
    if (caller == null) {
        return null;
    }
    return caller.getClassLoader0();
}

// Class 类的 getClassLoader0() 如下所示:
ClassLoader getClassLoader0() { return this.classLoader; }

// Class 类中定义的 classLoader 如下所示
private final ClassLoader classLoader;

6 优缺点

优点

  • 降低系统复杂性:外观模式为 子系统 中的一组接口提供了一个 统一的接口,使得 客户端子系统 之间的交互变得 简单客户端 只需要与 外观类 交互,而不需要了解 子系统 内部的复杂结构和实现细节,从而降低了系统的 复杂性
  • 提高系统的复用性:通过提供一个简单的接口,外观模式 使得子系统更加易于复用,客户端可以更轻松地达成目的。
  • 解耦客户端与子系统:外观模式实现了 客户端子系统 之间的解耦。客户端 不再 直接依赖子系统 的具体实现,而是通过 外观类子系统 交互。
  • 提高了系统的 灵活性 和 可维护性:当 子系统 内部发生变化时,只要 外观类 的接口保持不变,客户端 代码就不需要修改。
  • 支持系统的分层设计:在分层架构中,外观模式可以 为每一层提供一个清晰的接口使得层与层之间的交互更加明确和简单。这有助于系统的整体架构设计和维护。
  • 简化遗留系统的接口:对于复杂的遗留系统,外观模式可以提供一个简化的接口,使得新系统能够更容易地与遗留系统交互。这有助于在保持遗留系统 稳定性 的同时,提高新系统的 开发效率复用性

缺点

  • 增加系统的维护成本:当 子系统 中的接口发生变化时,可能需要修改 外观类 的实现。如果 外观类 封装了多个 子系统 的接口,那么这种修改可能会比较 复杂耗时
  • 可能隐藏子系统的重要功能:由于 外观类 封装了 子系统 的多个接口,可能会隐藏一些 子系统 的重要功能。如果 客户端 需要直接访问这些功能,可能需要绕过 外观类,这可能会 破坏封装性
  • 可能不符合开闭原则:在某些情况下,如果 子系统 中的接口频繁发生变化,而 外观类 的设计又没有充分考虑到这种变化,那么 外观类 可能需要频繁地进行修改,这可能会 违反开闭原则对扩展开放,对修改关闭)。
  • 可能导致性能问题:如果 外观类 封装了多个 子系统 的接口,并且这些接口在性能上存在差异,那么 外观类 的实现可能需要仔细考虑性能优化问题,否则,可能会导致 性能瓶颈不必要的性能损失

7 适用场景

  • 复杂系统的简化访问:在设计初期阶段,如果系统预计会变得复杂,可以考虑使用外观模式来简化对系统的访问。例如,在 经典的三层架构 中,可以在 数据访问层业务逻辑层业务逻辑层表示层 之间建立外观类,以提供简单的接口,降低耦合度。
  • 遗留系统的维护与扩展:对于难以维护和扩展的遗留系统,如果它包含重要的功能且新系统的开发必须依赖它,可以使用外观模式来简化其接口。通过 为遗留系统提供一个外观类,新系统可以与外观类交互,而无需直接了解遗留系统的复杂内部结构。
  • 客户端 与 子系统 解耦:当 客户端 需要与多个 子系统 交互时,如果 直接依赖 这些 子系统 的具体实现,会导致 客户端子系统 之间的 耦合度过高。使用外观模式可以 解耦 客户端子系统 之间的关系,客户端 只需与 外观类 交互,而无需了解 子系统 的内部实现。

8 总结

外观模式 是一种 结构型 设计模式,它为子系统的复杂调用 提供统一接口,使客户端能够更加方便地达成目的,而无需关心使用子系统的具体细节,从而降低了系统的 耦合性,提高了系统的 可维护性。但是,在使用 外观模式 时,要保证 合理封装 子系统的复杂调用,如果封装不合理,会降低系统的可维护性,违背 开闭原则

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1974426.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PointNet点云语义分割

在本教程中&#xff0c;我们将学习如何在斯坦福 3D 室内场景数据集 (S3DIS) 上训练 Point Net 进行语义分割。S3DIS 是一个 3D 数据集&#xff0c;包含来自多栋建筑的室内空间点云&#xff0c;占地面积超过 6000 平方米 [1]。Point Net 是一种新颖的架构&#xff0c;它使用整个…

基于JAVA的陶瓷工厂进销存管理系统的设计与实现

点击下载源码 基于JAVA的陶瓷工厂进销存管理系统的设计与实现 摘 要 时代在进步&#xff0c;我们的生产生活方式当然也要相对应的做出改变了。在今天这样一个信息化的时代&#xff0c;计算机软件已经广泛的被用于日常的办公&#xff0c;仓库的库存管理&#xff0c;企业的人员…

2024年热门开放式耳机评测!悠律、韶音、声阔到底该选谁?

开放式耳机选购技巧篇&#xff0c;可参考选购&#xff01; 作为一名数码评测博主&#xff0c;这两年用过的开放式耳机不下50款了&#xff0c;市面上的开放式耳机众多&#xff0c;很多人不知道该如何选择&#xff0c;其实选购都是有一定的技巧和规律性的&#xff0c;看配置就能…

无损下载器1.1.0.0(3.6M)支持批量下载无损音乐

无损音乐下载器。只有3.6M&#xff0c;简单试了一下感觉非常好用&#xff0c;不知道论坛里发过没有&#xff0c;也不知道作者是谁&#xff0c;非常感谢该软件的开发者&#xff01; 软件标题&#xff1a;无损下载器 版本号&#xff1a;1.1.0.0 使用步骤&#xff1a; 我们下载…

AVL解析

本节主要看板书 概念 AVL树&#xff08;Adelson-Velsky and Landis tree&#xff09;是一种自平衡二叉查找树&#xff0c;用于在动态集合中进行高效的插入、删除和查找操作。它保持树的高度接近最小可能值&#xff0c;从而确保这些操作的时间复杂度始终保持在O(log n)。AVL树…

OS—磁盘和固态硬盘

目录 一. 磁盘二. 磁盘的管理磁盘初始化分区引导块坏块 三. 磁盘调度算法磁盘存取时间磁盘调度算法先来先服务&#xff08;FCFS&#xff09;算法最短寻道时间优先&#xff08;Shorted Seek Time First,SSTF&#xff09;算法扫描&#xff08;SCAN&#xff09;算法LOOK 调度算法循…

30个可以帮程序员查询很多真相的网址

具体请前往&#xff1a;一站式综合查询导航 - 快递物流查询,国际区号查询,车牌号查询,航班动态查询,教育考试成绩和证书、学历、食品药品标准,招投标,知识产权,专利文献,企业信用,法律文书在线查询

13. 基于标定板的lidar到车体的外参标定思路

目录 1. 什么是lidar到车体的外参&#xff1f;2. 为什么要做这个外参矫正&#xff1f;3. 怎么做这个外参矫正&#xff1f;3.1 标定思路3.2 lidar检测标定板上的圆心流程介绍3.3 匹配过程 4. 老乡别走&#xff0c;一起来读书吧 1. 什么是lidar到车体的外参&#xff1f; 在机器人…

猫头虎分享疑难杂Bug:ERROR: No matching distribution found for beautifulsoup4解决方案

&#x1f42f; 猫头虎分享疑难杂Bug&#xff1a;ERROR: No matching distribution found for beautifulsoup4解决方案 摘要 Python开发过程中&#xff0c;ERROR: No matching distribution found for beautifulsoup4 是常见错误之一。本文将详细介绍此错误的产生原因及解决方…

2024最详细的安装教程来了!手把手教你安装Python和PyCharm

最详细的Python安装教程 一、进入Python官网首页&#xff0c;下载最新的Python版本 https://www.python.org/downloads/ 选择最新的Python3.10.5&#xff0c;下载64位的版本 二、下载完成后&#xff0c;进行安装 1.双击Python-3.10.5-amd64.exe 2.选择Customize installation…

入门 PyQt6 看过来(案例)21~ 绘图案例

今天带给大家的是一些绘制图形的案例&#xff0c;第一个是绘制奥运图片&#xff0c;第二个是绘制五角星&#xff0c;第三个是绘制时钟。 1 绘制奥运图片 源码&#xff1a; import sys from PyQt6.QtWidgets import QApplication, QWidget from PyQt6.QtCore import Qt, QRect…

CSS mask-image 实现边缘淡出过渡效果

使用场景 在生产环境中&#xff0c;遇到一个需求&#xff0c;需要在一个深色风格的大屏页面中&#xff0c;嵌入 Google Maps。为了减少违和感&#xff0c;希望地图四边能够淡出过渡。 这里的“淡出过渡”&#xff0c;关键是淡出&#xff0c;而非降低透明度。 基于 Google Ma…

科普文:微服务之Spring Cloud Alibaba组件Nacos一致性协议Distro+Raft概叙

一、概要 Nacos是阿里开放的一款中间件&#xff0c;它主要提供三种功能&#xff1a;持久化节点注册&#xff0c;非持久化节点注册和配置管理。 二、一致性协议 - AP/CP Nacos不是纯粹的AP服务&#xff0c;也不是纯粹的CP服务&#xff0c;而是两者同时支持。 这要从服务注册…

【学习日记】静态库与动态库的区别及使用指南

文章目录 静态库与动态库的区别及使用指南静态库定义使用方式优点缺点使用示例创建静态库使用静态库 动态库定义工作原理优点缺点使用示例创建动态库使用动态库 如何区分静态库和动态库总结 封面 静态库与动态库的区别及使用指南 本文将详细介绍这两种库的定义、工作原理、优缺…

【机器学习】为什么使用Scikit-Learn来进行逻辑回归以及如何使用Scikit-Learn进行逻辑回归

引言 在Scikit-Learn中&#xff0c;逻辑回归是通过LogisticRegression类实现的。该类提供了多种方法来训练模型、进行预测以及评估模型性能。用户可以自定义许多参数&#xff0c;包括正则化类型&#xff08;L1、L2或弹性网&#xff09;、求解器类型&#xff08;用于优化问题&am…

Cesium初探

Cesium 是一个开源 JavaScript 库&#xff0c;用于创建 3D 地理空间应用程序。它允许开发者在 Web 浏览器中构建高性能、交互式的 3D 地图和地球可视化应用&#xff0c;而无需安装任何插件。Cesium 支持多种数据格式&#xff0c;包括 3D Tiles&#xff08;一种高效的 3D 场景流…

变量作用域、作用域链、return

全局变量 全局变量因为在全局操作会每次留存上次操作的结果 局部变量因为执行完成就会被销毁并不会保留本次操作的结果 可以通过传参和返回&#xff0c;将结果不断地专递处理 局部变量 参数也是局部变量 函数内的预解析预赋值 函数内的局部变量 如果同名全局变量遇到局部变量…

Java的jstat命令输出GC信息时携带时间信息(Windows系统中)

之前写了一篇在Linux系统中携带时间的文章&#xff1a;Java的jstat命令输出GC信息时携带时间信息&#xff08;Linux系统中&#xff09; 但是很多时候&#xff0c;我们都是在Windows系统中开发&#xff0c;可能有些人没有Linux环境&#xff0c;所以这篇文章就讲一下在Windows系统…

[论文精读]Multi-View Multi-Graph Embedding for Brain Network Clustering Analysis

论文原文&#xff1a;3504035.3504050 (acm.org) 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&#xff0c;若有发现欢迎评论指正&#xff01;文章偏向于笔记&#xff0c;谨慎食用 目录 1. 省流版 1.1. 心得…

63 epoll服务器 (ET模式)

基于LT模式修改&#xff0c;并加入前面的应用层计算器&#xff0c;实现稍完整的服务器功能 1.修改tcp_socket.hpp&#xff0c;新增非阻塞读和非阻塞写接口 2.对于accept返回的new_sock加上EPOLLET这样的选项 注意&#xff1a;此代码暂时未考虑listen_sock ET的情况&#xff0c…