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

news/2025/2/12 16:15:21/

外观模式

  • 一、介绍
  • 二、家庭影院项目案例使用
  • 三、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.ppmy.cn/news/1381392.html

相关文章

UE5数字孪生系列笔记(二)

智慧城市数字孪生系统 制作流云动画效果 首先添加一个图像在需要添加流云效果的位置 添加动画效果让其旋转 这个动画效果是程序开始就要进行的,所以要在EventConstruct中就可以启动这个动画效果 添加一个一样的图像在这里,效果是从此处进行放大消散 添…

TIOBE 3月编程排行榜出炉:遥遥领先,霸榜第一!

哈喽呀~又到了每个月的语言排行榜啦! TIOBE 3月编程语言也已经公布,具体有啥新变化呢?快和我一起往下看~ Python遥遥领先,霸榜第一 先来看看本月排行榜top5的表现。毫无疑问,Python依然遥遥领先: Python 第…

Caffeine本地缓存快速上手教程,通俗易懂

1. 概述 使用缓存的优点是可以减少直接访问数据库的压力。Caffeine是目前单机版缓存性能最高的,提供了最优的缓存命中率。用法和java中的map集合比较类似,底层使用一个ConcurrencyHashMap来保存所有数据,可以理解为一个增强版的map集合&…

linux使用samba实现共享文件夹

在Linux上设置共享文件夹可以使用多种方法,这里我将介绍一种基于Samba的方法,因为Samba是一个在Linux和Windows系统之间实现文件共享的流行工具。 以下是在Linux上设置共享文件夹的一般步骤: 安装Samba: sudo apt update sudo …

Maya 切换面选择模式

文章目录 切换面选择模式 切换面选择模式 maya默认的面选择模式是点击面选择面,但是这种模式下,在线框显示时我们会不知道哪里有面,我们更希望点击面的中心点选择面,就像下图,哪里有面一目了然 设置方法 这里可以切…

基于SpringBoot的“私人健身与教练预约管理系统”的设计与实现(源码+数据库+文档+PPT)

基于SpringBoot的“私人健身与教练预约管理系统”的设计与实现(源码数据库文档PPT) 开发语言:Java 数据库:MySQL 技术:SpringBoot 工具:IDEA/Ecilpse、Navicat、Maven 系统展示 系统首页界面图 用户注册界面图 健…

关于某古桥自动化监测保护的行动建议书

目 录 1 概述... 1 1.1 项目背景... 1 1.2 项目建设的必要性... 2 1.3 古桥保护的价值和意义... 3 1.3.1 古桥保护的价值... 3 1.3.2 古桥保护的意义... 4

Centos7 安装mongodb 7.0

官方手册参考: https://www.mongodb.com/docs/manual/tutorial/install-mongodb-on-red-hat/ Mongodb支持的版本 安装 MongoDB 社区版 按照以下步骤使用包管理器安装 MongoDB Community Edition yum。 配置包管理系统 ( yum) 创建一个/etc/yum.repos.d/mongodb-o…