重读 Java 设计模式: 探索经典之道与 Spring 框架的设计

写在开头

记得大学刚毕业那会儿,想学点东西,于是拿出了《Head First 设计模式》这本书,就开始了阅读,我曾对这些模式感到晦涩难懂。然而,随着工作岁月的增长,我逐渐领悟到设计模式的价值,尤其是在实践中,特别是在Spring这样的大型设计框架中。

刚开始接触设计模式时,我常常感到困惑。这些模式的概念和实现方式似乎遥不可及,就像是编程世界中的高塔一样,让人望而却步。然而,随着不断地学习和实践,我渐渐明白了设计模式的真正价值所在。

设计模式并不仅仅是一些理论概念,它们是一种解决常见问题的实用方法,是编写优雅、高效代码的利器。在工作中,我越来越多地意识到,设计模式不仅仅是理论上的东西,而是可以直接应用于实践的工具。

特别是在与Spring框架的设计与开发中,设计模式发挥了极其重要的作用。Spring框架本身就是一个设计模式的典范,它采用了诸如依赖注入、工厂模式、代理模式等多种设计模式,使得框架具有高度的灵活性和可扩展性。

因此,我想借此机会将我在设计模式与Spring框架结合实践中所获得的经验分享给大家。通过这个专栏,我希望能够帮助更多的人理解设计模式的精髓,以及它们在实际项目中的应用。让我们一起探索设计模式的奥秘,以及它们在大型框架设计中的实战价值!

该专栏按照如下大纲进行编写,首先会介绍设计原则,在理解完设计原则后,我们深入了解每一种设计模式及其在 Spring 框架中的应用。

image-20240303225038336

从设计原则出发

设计模式的核心是一系列设计原则,它们为软件设计提供了基本的指导方针。在阅读Spring框架的设计时,我们也将遵循这些设计原则,并结合Spring框架的实践,探讨如何将这些设计原则应用于框架的设计与实现。

1. 单一职责原则(Single Responsibility Principle - SRP)

Spring 框架中的各个组件(如控制器、服务、数据访问对象等)都遵循了单一职责原则,每个组件都专注于执行特定的任务,从而提高了代码的内聚性和可维护性。

这里给出 Spring 框架中几个类,大家去感受一下:

  • XmlBeanDefinitionReader : 负责加载 XML 类型资源的 BeanDefinition(Bean 的元信息)。
  • AutowiredAnnotationBeanPostProcessor : 负责 @Autowired 注解注入依赖的实现(方法注入、字段注入)。

2. 开放-封闭原则(Open-Closed Principle - OCP)

Spring框架通过面向接口编程和依赖注入等机制,实现了对扩展的开放和对修改的封闭。框架的核心功能可以在不修改原有代码的情况下进行扩展和定制,从而提高了系统的可扩展性和灵活性。

这一项,Spring 中更是到处可见,就拿 BeanFactory 体系来举例,我们先来看下 BeanFactory 及其部分派生接口:

image-20240303230000486

大家在 Spring 源码中可以自行搜索看下每个接口中的功能定义,每个接口各司其职(单一职责),新增功能不会堆在一个接口内,例如 ListableBeanFactory 继承 BeanFactory,在负责 BeanFactory 功能的同时,扩展了 Bean 集合查找的特性。

3. 里氏替换原则(Liskov Substitution Principle - LSP)

Spring 框架中的各个组件都遵循了里氏替换原则,子类对象可以替换父类对象并且不影响程序的正确性。这样保证了框架的稳定性和可扩展性。

这里给大家举个例子:

public class Demo {public static void main(String[] args) {DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();// 重点关注 XmlBeanDefinitionReader 构造器XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);reader.loadBeanDefinitions("classpath:/META-INF/merged-bean-definition.xml");SuperUser superUser = beanFactory.getBean("superUser", SuperUser.class);System.out.println(superUser);}
}
// 在构造器中,实际入参为 BeanDefinitionRegistry
public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {super(registry);
}

如上述示例待所示,XmlBeanDefinitionReader 构造器的形参要求是 BeanDefinitionRegistry,实际上我们传入的是 DefaultListableBeanFactory,程序照常运行,这就体现里氏替换的设计原则,DefaultListableBeanFactory 是 BeanDefinitionRegistry 的子类。

4. 依赖倒置原则(Dependency Inversion Principle - DIP)

Spring框架通过控制反转(IoC)和依赖注入(DI)等机制,实现了依赖倒置原则。高层模块不依赖于低层模块的具体实现,而是依赖于抽象,从而降低了模块之间的耦合度,提高了系统的灵活性和可维护性。

这个大家相比更深有体会了,不依赖具体实现也就意味着我们可以任意变更其具体实现来控制程序的不同行为而应用不受影响,降低了模块之间的耦合度。下面给大家举个例子。

package com.markus.desgin.mode;/*** @author: markus* @date: 2024/3/3 11:15 PM* @Description:* @Blog: https://markuszhang.com* It's my honor to share what I've learned with you!*/
public class Demo {Animal animal;public Demo(Animal animal) {this.animal = animal;}public void cry() {this.animal.cry();}public static void main(String[] args) {Demo demo = new Demo(new People());demo.cry();demo = new Demo(new Dog());demo.cry();demo = new Demo(new Cat());demo.cry();}interface Animal {void cry();}static class People implements Animal {@Overridepublic void cry() {System.out.println("呜呜呜~");}}static class Dog implements Animal {@Overridepublic void cry() {System.out.println("汪汪汪~");}}static class Cat implements Animal {@Overridepublic void cry() {System.out.println("喵喵喵~");}}
}

5. 接口隔离原则(Interface Segregation Principle - ISP)

Spring框架合理设计了各个组件之间的接口,遵循了接口隔离原则。每个接口只包含客户所需要的方法,避免了不必要的依赖关系,提高了系统的灵活性和可维护性。

还拿刚才的 BeanDefinitionReader 举例,它依赖 BeanDefinitionRegistry,而我们知道 BeanDefinitionRegistry 在 Spring 框架中的唯一实现就是 DefaultListableBeanFactory。而在设计的时候遵循了接口隔离原则,并没有将 DefaultListableBeanFactory 写在这里。BeanDefinitionReader 的作用就是读取指定的资源解析出 BeanDefinition 并将其注册到 IoC 容器中,而 BeanDefinitionRegistry 就是干这活的哈哈,所以这里就引入了 BeanDefinitionRegistry。

public interface BeanDefinitionRegistry extends AliasRegistry {/*** Register a new bean definition with this registry.* Must support RootBeanDefinition and ChildBeanDefinition.* @param beanName the name of the bean instance to register* @param beanDefinition definition of the bean instance to register* @throws BeanDefinitionStoreException if the BeanDefinition is invalid* @throws BeanDefinitionOverrideException if there is already a BeanDefinition* for the specified bean name and we are not allowed to override it* @see GenericBeanDefinition* @see RootBeanDefinition* @see ChildBeanDefinition*/void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)throws BeanDefinitionStoreException;/*** Remove the BeanDefinition for the given name.* @param beanName the name of the bean instance to register* @throws NoSuchBeanDefinitionException if there is no such bean definition*/void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;/*** Return the BeanDefinition for the given bean name.* @param beanName name of the bean to find a definition for* @return the BeanDefinition for the given name (never {@code null})* @throws NoSuchBeanDefinitionException if there is no such bean definition*/BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;/*** Check if this registry contains a bean definition with the given name.* @param beanName the name of the bean to look for* @return if this registry contains a bean definition with the given name*/boolean containsBeanDefinition(String beanName);/*** Return the names of all beans defined in this registry.* @return the names of all beans defined in this registry,* or an empty array if none defined*/String[] getBeanDefinitionNames();/*** Return the number of beans defined in the registry.* @return the number of beans defined in the registry*/int getBeanDefinitionCount();/*** Determine whether the given bean name is already in use within this registry,* i.e. whether there is a local bean or alias registered under this name.* @param beanName the name to check* @return whether the given bean name is already in use*/boolean isBeanNameInUse(String beanName);}

6. 最少知识原则(Least Knowledge Principle - LKP)

Spring框架通过合理设计组件之间的关系,遵循了最少知识原则。对象之间的依赖关系尽可能简化,每个对象对其他对象有尽可能少的了解,从而提高了系统的可维护性和可扩展性。

意思就是除了该类该暴露出去的方法,将其余方法的访问权限设置为私有,不让使用该类的对象感知。

image-20240303232951089

本文总结

总结一下,本文是《重读 Java 设计模式:探索经典之道与 Spring 框架的设计》的第一篇,算是开了个头,主要介绍了写这个专栏的初衷以及设计原则。

设计模式的演进是一个不断迭代、不断优化的过程。在接下来的系列文章中,我们将深入探讨经典的设计模式,并结合Spring框架的实践,重新阅读和重写它们,以期提供更加灵活、可维护和高效的解决方案。让我们一起踏上这段设计之旅,探索设计模式与Spring框架的结合之美!

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

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

相关文章

使用 Docker 部署 File Browser 文件管理系统

1)File Browser 介绍 官网:https://filebrowser.org/ GitHub:https://github.com/filebrowser/filebrowser 今天为大家分享一款开源的私有云盘项目:File Browser,简单实用、轻量级、跨平台,安装部署简单快…

Mysql按照月份分组统计数据,当月无数据则填充0

目录 起因实现结论 起因 最近有个需求需要在sql中实现获取近半年的统计数据,本来以为挺简单的,不过这个项目数据基本没有,在此情况下还要实现获取近半年的数据就没办法简单group by了 实现 #如果每个月都有数据的话是比较简单的 SELECT DA…

Request和Response对象

Request和Response都是Servlet的service方法的参数,Request负责获取请求数据,而Response负责设置相应数据~ 一.Request 1.继承体系 Tomcat负责解析数据,因此由Tomcat来提供实现类~ 2.获取请求数据 请求行 请求头 请求体 需要注意的是只有…

【方法】如何打开7Z分卷压缩文件?

什么是7Z分卷压缩文件?就是在压缩文件时,将文件压缩成若干个大小一样、以“文件名.7z.序号”格式命名的7Z压缩包,可以方便存储和传输,如下图所示。 一、7Z分卷压缩文件如何打开? 我们只需要按照普通压缩包的打开方式&…

C语言分析基础排序算法——插入排序

目录 插入排序 直接插入排序 希尔排序 希尔排序基本思路解析 希尔排序优化思路解析 完整希尔排序文件 插入排序 直接插入排序 所谓直接插入排序,即每插入一个数据和之前的数据进行大小比较,如果较大放置在后面,较小放置在前面&#x…

带胶囊按钮的标题是如何实现的

使用uni-app开发小程序经常会遇到胶囊按钮和标题之间融合的问题,因为这样可以大大提高页面的美观和整体性,那么接下来简单拆分步骤看下是如何实现的吧 😁 可以看到我们设置的标题是在默认标题栏之下的(这不是我们想要的效果 &…

Python学习笔记-Flask实现简单的抽奖程序

1.导入flask包和randint包 from flask import Flask,render_template from random import randint 2.初始化 Flask 应用: app Flask(__name__) 3. 定义英雄列表 hero [黑暗之女,狂战士,正义巨像,卡牌大师,德邦总管,无畏战车,诡术妖姬,猩红收割者,远古恐惧,正义天使,无极剑…

【广度优先搜索】【图论】【并集查找】2493. 将节点分成尽可能多的组

作者推荐 视频算法专题 本文涉及知识点 广度优先搜索 图论 并集查找 LeetCod2493. 将节点分成尽可能多的组 给你一个正整数 n ,表示一个 无向 图中的节点数目,节点编号从 1 到 n 。 同时给你一个二维整数数组 edges ,其中 edges[i] [ai…

S4---FPGA-K7板级原理图硬件实战

视频链接 FPGA-K7板级系统硬件实战01_哔哩哔哩_bilibili FPGA-K7板级原理图硬件实战 基于XC7K325TFFG900的FPGA硬件实战框图 基于XILINX 的KINTEX-7 芯片XC7K325FPGA的硬件平台,FPGA 开发板挂载了4 片512MB 的高速DDR3 SDRAM 芯片,另外板上带有一个SODIM…

考虑到通信链路中断的(Delay Tolerant Network, DTN)

文章目录 A Study of DTN for Reliable Data Delivery from Space Station to Ground Stationabstractintroduction An Analytical Framework for Disruption of Licklider Transmission Protocol in Mars Communicationsabstract本文贡献 OVERVIEW OF RELIABLE DATA TRANSMISS…

资源池化单集群安装经验总结

登录linux环境通过跳板机用ssh命令连接分配的ip,将准备好的openGauss安装包以及xml文件上传到跳板机用scp命令传到自己的节点。 问题1:预安装包版本问题 解决方法:查看自己机器的系统,至openGauss官网下载正确版本的安装包 open…

Java使用spire.doc操作word文档(合并、插入文字图片和表格、替换书签)

一、引入依赖 <dependency><groupId>e-iceblue</groupId><artifactId>spire.office</artifactId><version>7.5.4</version> </dependency> 二、word操作 1、合并word文档 import com.spire.doc.Document; import com.spir…

目标检测——摩托车头盔检测数据集

一、简介 首先&#xff0c;摩托车作为一种交通工具&#xff0c;具有高速、开放和稳定性差的特点&#xff0c;其事故发生率高&#xff0c;伤亡率排在机动车辆损伤的首位。因此&#xff0c;摩托车乘员头盔对于保护驾乘人员头部安全至关重要。在驾乘突发状况、人体受冲击时&#…

#14vue3生成表单并跳转到外部地址的方式

1、背景 后端返回的json数据中包含一个json数组&#xff0c;此数组中是目标跳转地址所需要的form表单的数据。 2、跳转前的页面 const goto () > {finish.value true;request.post(/xxx/yyy,{zzz: zzz.value}).then(res > {const url res.data.submitUrlconst params…

在线安装MySQL5.7

在线安装MySQL 安装MySQL5.7 yum -y install mysql57-community-release-el7-10.noarch.rpm 若无可用安装包&#xff0c;执行下面这句 wget http://dev.mysql.com/get/mysql57-community-release-el7-7.noarch.rpm 本地安装 yum localinstall -y mysql57-community-releas…

python异常机制

当代码出现异常后底下代码都不会被执行了&#xff0c;也就是程序崩溃了。当然能避免异常的话尽量避免但是有的时候这个是没有办法避免的。 异常处理 &#xff08;注&#xff1a;异常处理是从上往下处理&#xff0c;所以编写代码时要注意&#xff09; 语法 try:可能出现异常…

【漏洞复现】锐捷 EWEB auth 远程命令执行漏洞

免责声明&#xff1a;文章来源互联网收集整理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该…

力扣面试经典150 —— 6-10题

力扣面试经典150题在 VScode 中安装 LeetCode 插件即可使用 VScode 刷题&#xff0c;安装 Debug LeetCode 插件可以免费 debug本文使用 python 语言解题&#xff0c;文中 “数组” 通常指 python 列表&#xff1b;文中 “指针” 通常指 python 列表索引 文章目录 6. [中等] 轮转…

MySQL基础-----约束

目录 前言 一、概述 二、约束演示 三、外键约束 1.介绍 2.语法 四、删除/更新行为 1.CASCADE 2.SET NULL 前言 本期我们开始MySQL约束的学习&#xff0c;约束一般是只数据键对本条数据的约束&#xff0c;通过约束我们可以保证数据库中数据的正确、有效性和完整性。 下面…

vite+vue3门户网站菜单栏动态路由控制

门户网站用户端需要分板块展示&#xff0c;板块内容由管理端配置&#xff0c;包括板块名称&#xff0c;访问路径&#xff0c;路由组件&#xff0c;展示顺序&#xff0c;是否展示。如下图所示&#xff1a; 用户访问门户网站时&#xff0c;展示菜单跳转通过板块配置&#xff0c;动…