Java外观模式源码剖析及使用场景

外观模式

  • 一、介绍
  • 二、家庭影院项目案例使用
  • 三、Java API或框架中应用分析
  • 三、Spring框架ApplicationContext源码

一、介绍

外观模式(Facade Pattern)是一种结构型设计模式,它为子系统中的一组接口提供了一个统一的高层接口,使得子系统更加容易使用。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

外观模式的主要作用有以下几点:

  1. 简化系统的调用复杂性。通过外观模式,客户端可以只需调用外观类中的方法就可以完成复杂的操作,无需深入了解子系统的内部工作机制。

  2. 减小系统的编译依赖。通过外观模式,客户端只需要与外观类发生编译依赖,而无须与子系统的其他模块发生直接依赖。

  3. 有利于体系结构的拓展。在有新的子系统加入时,只需创建一个新的外观类,客户端无须修改源代码,减少了客户端与子系统的耦合关系。

外观模式主要包含以下几种角色:

  1. 外观(Facade)角色:外观角色需要知道所有子系统的功能和职责,它是一个独立的模块,它与系统中的其他模块一起一起,构成了一个更大的系统。

  2. 子系统(Sub System)角色:子系统角色实现系统的部分功能,并可以和其他子系统协作以完成更复杂的功能。子系统角色不需要知道外观的存在。

  3. 客户(Client)角色:客户端通过外观模式访问子系统的功能。

下面是一个外观模式的简单示例:

// 子系统角色
class SubSystemA {public void operationA() {System.out.println("SubSystemA.operationA()");}
}class SubSystemB {public void operationB() {System.out.println("SubSystemB.operationB()");}
}class SubSystemC {public void operationC() {System.out.println("SubSystemC.operationC()");}
}// 外观角色
class Facade {private SubSystemA a = new SubSystemA();private SubSystemB b = new SubSystemB();private SubSystemC c = new SubSystemC();public void operation() {a.operationA();b.operationB();c.operationC();}
}// 客户端
public class Client {public static void main(String[] args) {Facade facade = new Facade();facade.operation();}
}

运行结果:

SubSystemA.operationA()
SubSystemB.operationB()
SubSystemC.operationC()

在这个示例中,Facade类充当了外观角色,它封装了子系统SubSystemASubSystemBSubSystemC的功能,并提供了一个简单的operation()方法供客户端调用。客户端只需要与外观Facade对象交互,而不需要了解子系统的内部细节。

外观模式的优点包括:

  1. 减少了系统与客户端的耦合度,使系统更加容易移植和维护。
  2. 通过为复杂子系统提供一个统一的入口,降低了客户端的使用难度。
  3. 客户端代码更加简洁,系统更加易于理解和维护。

缺点包括:

  1. 不符合开闭原则,如果增加新的子系统功能,可能需要修改外观类源码。
  2. 外观类的编写比较困难,需要全面了解子系统的功能和职责。

二、家庭影院项目案例使用

需求:一个家庭影院系统,它包含了音响系统、投影仪系统和DVD播放器系统等子系统。我们需要提供一个统一的接口,让用户可以方便地控制整个家庭影院系统。

1. 定义子系统

首先,我们定义音响系统、投影仪系统和DVD播放器系统的接口和实现类:

// 音响系统
interface AudioSystem {void turnOn();void turnOff();void setVolume(int volume);
}class AudioSystemImpl implements AudioSystem {// 实现具体的音响系统操作
}// 投影仪系统
interface ProjectorSystem {void turnOn();void turnOff();void setInput(String input);
}class ProjectorSystemImpl implements ProjectorSystem {// 实现具体的投影仪系统操作
}// DVD播放器系统
interface DVDPlayer {void turnOn();void turnOff();void play(String movie);
}class DVDPlayerImpl implements DVDPlayer {// 实现具体的DVD播放器操作
}

2. 定义外观类

接下来,我们定义一个HomeTheaterFacade类作为外观,它封装了音响系统、投影仪系统和DVD播放器系统的操作:

class HomeTheaterFacade {private AudioSystem audioSystem;private ProjectorSystem projectorSystem;private DVDPlayer dvdPlayer;public HomeTheaterFacade(AudioSystem audioSystem, ProjectorSystem projectorSystem, DVDPlayer dvdPlayer) {this.audioSystem = audioSystem;this.projectorSystem = projectorSystem;this.dvdPlayer = dvdPlayer;}public void watchMovie(String movie) {audioSystem.turnOn();projectorSystem.turnOn();projectorSystem.setInput("DVD");dvdPlayer.turnOn();dvdPlayer.play(movie);}public void endMovie() {audioSystem.turnOff();projectorSystem.turnOff();dvdPlayer.turnOff();}
}

HomeTheaterFacade类中,我们提供了watchMovieendMovie两个方法,分别用于启动和关闭家庭影院系统。这些方法封装了对各个子系统的调用,简化了系统的使用复杂度。

3. 使用外观类

最后,在客户端代码中,我们可以直接使用HomeTheaterFacade类来控制整个家庭影院系统:

public class Client {public static void main(String[] args) {AudioSystem audioSystem = new AudioSystemImpl();ProjectorSystem projectorSystem = new ProjectorSystemImpl();DVDPlayer dvdPlayer = new DVDPlayerImpl();HomeTheaterFacade homeTheater = new HomeTheaterFacade(audioSystem, projectorSystem, dvdPlayer);homeTheater.watchMovie("机器人总动员");// 观看电影...homeTheater.endMovie();}
}

在上面的代码中,我们创建了音响系统、投影仪系统和DVD播放器系统的实例,然后将它们传递给HomeTheaterFacade构造函数。客户端只需要与HomeTheaterFacade对象交互,通过调用watchMovieendMovie方法即可控制整个家庭影院系统。

使用外观模式,我们将复杂的子系统操作封装在HomeTheaterFacade类中,客户端无需了解各个子系统的内部细节,从而降低了系统的使用复杂度。同时,如果需要增加或修改子系统,只需要修改外观类,而无需更改客户端代码,提高了系统的可维护性和可扩展性。

三、Java API或框架中应用分析

  1. Java I/O库

Java I/O库中广泛使用了外观模式。例如java.io.File类就是一个外观,它提供了对文件系统进行操作的简化方法,而无需直接面对复杂的操作系统底层API。

File file = new File("example.txt");
file.createNewFile(); // 创建新文件
file.delete(); // 删除文件

通过File对象,我们可以方便地执行文件的创建、删除等操作,而不用关心底层的具体实现细节。

  1. Java 数据库连接(JDBC)

Java JDBC中的DriverManager类充当了数据库连接的外观角色。它封装了获取数据库连接的复杂过程,为我们提供了一个简单的接口。

Connection conn = DriverManager.getConnection(url, username, password);

通过DriverManager.getConnection()方法,我们可以获取到一个数据库连接对象,而不需要关注加载驱动、创建连接等繁琐步骤。

  1. Spring框架

Spring框架中的ApplicationContext接口可以看作是一个外观。它为开发者提供了获取Spring Bean的统一入口,而隐藏了Bean的创建、配置、装配等复杂细节。

ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
MyService service = (MyService) context.getBean("myService");

通过ApplicationContext对象,我们可以方便地获取Spring管理的Bean实例,而无需了解Spring内部的工作机制。

  1. Java Servlet

在JavaEE的Servlet规范中,ServletRequestServletResponse接口可以看作是请求和响应对象的外观。它们为开发者提供了一系列方法来访问HTTP请求和响应的各种属性,而无需直接处理底层的HTTP协议细节。

protected void doGet(HttpServletRequest request, HttpServletResponse response) {String param = request.getParameter("name");response.setContentType("text/html");// ...
}

通过ServletRequestServletResponse对象,我们可以方便地获取请求参数、设置响应头等,而无需关注HTTP协议的具体实现细节。

三、Spring框架ApplicationContext源码

ApplicationContext接口可以被视为一个外观(Facade)模式的典型应用。它为开发者提供了一个统一的入口来访问Spring容器中的Bean实例,而隐藏了Bean的创建、装配、初始化等复杂细节。分析一下ApplicationContext接口的源码实现,以深入理解它是如何运用外观模式的。

ApplicationContext接口继承自BeanFactory接口,它定义了一些基本的方法,如getBean()containsBean()等,用于获取和检查容器中的Bean。但是,ApplicationContext接口还提供了一些额外的功能,如访问资源文件、发布事件等。这些功能由ApplicationContext接口的不同实现类完成,如ClassPathXmlApplicationContextAnnotationConfigApplicationContext等。

我们以ClassPathXmlApplicationContext为例,看一下它的实现细节:

public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {// ...@Overrideprotected Resource getResourceByPath(String path) {// 获取classpath资源return new ClassPathResource(path);}// ...
}public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {// ...@Overrideprotected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {// 加载Bean定义XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);beanDefinitionReader.loadBeanDefinitions(getConfigResources());}// ...
}

从上面的源码可以看出,ClassPathXmlApplicationContext实现了getResourceByPath()方法,用于从classpath中加载资源文件。而loadBeanDefinitions()方法则负责从资源文件中加载Bean定义。这些复杂的实现细节都被封装在ApplicationContext接口的具体实现类中,对外部客户端来说是透明的。

客户端只需要直接使用ApplicationContext接口提供的方法即可,如:

ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
MyService service = context.getBean("myService", MyService.class);

在上面的代码中,客户端仅需创建一个ApplicationContext实例,并通过getBean()方法获取所需的Bean实例,而不必关心Bean的创建、装配、初始化等复杂过程。

从这个角度来看,ApplicationContext接口扮演了外观角色,它为客户端提供了一个统一的入口来访问Spring容器中的Bean,同时隐藏了Bean加载和管理的复杂细节。这种设计有效地降低了客户端代码与Spring容器实现之间的耦合度,提高了代码的可维护性和可扩展性。

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

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

相关文章

leetcode:二叉树的左右子树反转的递归和迭代的C++实现

问题描述 给定一个二叉树,将其每个节点的左右子树进行反转。 解决方案 以下是 C 代码实现: TreeNode* invertTree(TreeNode* root) {if (root nullptr) {return nullptr;}// 交换当前节点的左右子树TreeNode* temp root->left;root->left r…

C语言转义字符:一文打尽

转义字符 1. 前言2. 预备知识2.1 打印格式2.2 进制转换2.3 ASCII码 3. 什么是转义字符4. 常见的转义字符4.1 \?4.2 \4.3 \"4.4 \\4.5 \dddddd表示1到3个八进制数字4.6 \xdddd表示1到2个十六进制数字4.7 其他转义字符 5. 一道笔试题6. 一个小插曲 1. 前言 大家好&#xf…

DFS和BFS以及练习题目(未完待续)

DFS和BFS 温馨提示:学习dfs之前最好先了解一下递归的思想。 递归思想 斐波那契 题目分析 题目代码 import java.util.Scanner; public class Main{static long dp[]; public static void main(String[] args) {Scanner scanner new Scanner(System.in);int t…

吴恩达deeplearning.ai:倾斜数据集的误差指标精确率、召回率

以下内容有任何不理解可以翻看我之前的博客哦:吴恩达deeplearning.ai专栏 文章目录 倾斜数据集的误差指标罕见病预测精确率和召回率 精确率和召回率的权衡精确率和召回率的矛盾关系 F1算法 倾斜数据集的误差指标 在神经网络中,如果你的数据集中正例和负…

CSS样式中长度单位含义解析:rpx、px、vw、vh、em、rem、pt

在 CSS 样式中,有几种常见的长度单位,包括 rpx 、 px 、 vw 和 vh 等,含义解析如下: 1 . rpx (响应像素): 是微信小程序中的一种相对长度单位,可以根据屏幕宽度进行自适应缩放。 1rp…

PTA 对于下列程序,正确的是() 。void f(int *p){ *p = 5;}int main(void){ int a, *p; a = 10;

对于下列程序,正确的是() 。 void f(int *p) {*p 5; } int main(void) {int a, *p;a 10;p &a;f(p);printf(“%d”, (*p));return 0; }A.5 B.6 C.10 D.11 答:A 解析:这里考察当是指针作为函数的参数。这里将 p …

python脚本批量关闭exe文件

python脚本批量关闭exe文件 1、安装psutil库 pip install psutil2、示例代码 """ @contact: 微信 1257309054 @file: main.py @time: 2024/3/9 21:16 @author: LDC """ import os import time import psutildef is_process_running(process_n…

docker删除、停止所有容器或镜像

docker删除、停止所有容器或镜像 列出所有的容器 ID docker ps -aq停止所有容器 docker stop $(docker ps -aq)删除所有容器 docker rm $(docker ps -aq)删除所有镜像 docker rmi $(docker images -aq)

算法之对于算法的想法

小插曲 很早就参加过头条的面试,真的很早以前,头条那时还不是很火,自己的首先也不是头条。就知道对算法特别的重视,现实也的确如此。记得还是面对面的面试, 忘记一面的算法题目了,不过当时是有思路的&…

Codeforces Round 719 (Div. 3)除F2题外补题报告

Codeforces Round 719 Div. 3 除F2题外补题报告 得分情况补题情况错题分析C题题目大意初次思路正解思路正解代码错误原因 D题题目大意初次思路正解思路正解代码错误原因 E题题目大意初次思路正解思路正解代码 F1题题目大意正解思路正解代码 G题题目大意正解思路正解代码 得分情…

EDA 许可证调度

背景 在芯片设计和HPC行业EDA软件的许可证是很昂贵的数据资源,如何高效利用许可证以及计算资源是系统管理员需要认真思考的问题。 商用解决方案 LSF是芯片和HPC行业流行的调度方案,LSF产品家族包含了许可证调度的功能。它提供了许可证管理和调度功能&…

开发知识点-C++之win32与NT内核

win32 Windows MFC编程 常用API汇总EnumWindows()函数UpdateData()函数static与 单例 设计模式函数原型:BOOL WINAPI SetConsoleTitle(__in LPCTSTR lpConsoleTitle);HWND 是一个基本类型 表示窗口句柄FindWindow函数SendMessage函数 将指定的消息发送到一个或多个窗口PostMes…

Matlab|配电网智能软开关(sop)规划模型

目录 1 主要内容 目标函数 2 部分程序 3 程序结果 3.1 sop选址定容优化模型 3.2 对比算例(不含sop) 4 下载链接 1 主要内容 该程序参考文献《基于改进灵敏度分析的有源配电网智能软开关优化配置》,采用二阶锥算法,以改进的…

蓝桥杯-最大距离

根据题意直接写出来就行。 简单题就话不多说上代码了 #include <iostream> #include <cmath> using namespace std; int main() {int n;cin>>n;int a[1000]{0};for(int i0;i<n;i){cin>>a[i];}int temp 0;int dis 0;for(int i0;i<n-1;i)for(…

JMeter—逻辑控制器

JMeter逻辑控制器 JMeter逻辑控制器可以对元件的执行逻辑进行控制&#xff0c;除仅一次控制器外&#xff0c;其他可以嵌套别的种类的逻辑控制器  一、ForEach控制器 定义一个循环规则&#xff0c;关键参数说明&#xff1a; 输入变量前缀&#xff1a;可以在“用户自定义的变量…

读书笔记:《思考 . 快与慢》- 3 过度自信与决策错误

《思考 . 快与慢》 [美] 丹尼尔 . 卡尼曼 著 胡晓姣 李爱民 何梦莹 译 这本书会改变你的思考方式 叙事谬误&#xff0c;用来描述有缺憾的往事是如何影响我们的世界观和我们对未来的预期的 能够吸引人们眼球的那些说法往往很通俗易懂&#xff0c;具体而不抽象 人类常会…

蓝桥杯刷题(二)

参考大佬代码&#xff1a;&#xff08;区间合并二分&#xff09; import os import sysn, L map(int, input().split()) # 输入n,len arr [list(map(int, input().split())) for _ in range(n)] # 输入Li,Si def check(Ti, arr, L)->bool:sec [] # 存入已打开的阀门在…

Golang中defer与return的执行顺序——不易混淆

前言 defer和return是常用的一个点&#xff0c;面试高频&#xff0c;也是实战容易踩坑。先前对他进行过一次学习&#xff0c;理解起来略拗口&#xff0c;但好歹也算是学会了。昨天面IEG时候被面试官拷打&#xff0c;并给出了一些提示&#xff0c;让我对个知识点有了新的认识&a…

JDK17镜像制作

背景 获取JDK17 wget https://download.oracle.com/java/17/latest/jdk-17_linux-x64_bin.tar.gz 解压JDK tar -zxvf jdk-17_linux-x64_bin.tar.gz 制作JRE 由于jdk的体积比较大&#xff0c;可以使用jre来作为运行环境&#xff0c;jdk1.8及以前版本&#xff0c;自带jre&#…

JVM-1

目录 1.基础知识 1.栈 2.本地方法栈 3.程序计数器 4.堆 5.方法区 6.JVM内存可见性 2.虚拟机类加载机制 1.加载 2.验证 3.准备 4.解析 5.初始化 6.使用 7.卸载 1.基础知识 JVM内存模型&#xff08;5种&#xff09;&#xff1a;栈&#xff0c;本地方法栈&#xff…