重读 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.rhkb.cn/news/271541.html

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

相关文章

类和对象(4)

文章目录 1. const成员2.取地址及const取地址操作符重载3. 再谈构造函数3.1构造函数体赋值3.2初始化列表 1. const成员 将const修饰的成员函数称为const成员函数。 const修饰类成员函数,实际修饰该成员函数的隐含地this指针,表明在该成员函数中不能对类…

【echarts】xAxis鼠标事件失效问题

项目中用到echarts柱状图,出现x轴标签文字过长重叠问题,在pass掉标签倾斜、换行方案之后最终决定限制文字长度,超出以…占位,鼠标悬浮时显示完整tooltip。 但编写过程中发现xAxis鼠标事件无法触发,只有bar区域是可触发…

【C++杂货铺】详解string

目录 🌈前言🌈 📁 为什么学习string 📁 认识string(了解) 📁 string的常用接口 📂 构造函数 📂 string类对象的容量操作 📂 string类对象的访问以及遍历操…

【uni-app】condition 启动模式配置,生产环境无效,仅开发期间生效

在小程序开发过程中,每次代码修改后,都会启动到首页,有时非常不方便,为了更高效的开发,有时需要模拟直接跳转到指定的页面, 操作方法如下: 在pages.joson里面配置下列代码: "…

解决 matplotlib 中文显示乱码的问题

matplotlib 库默认只显示中文 例如: import matplotlib.pyplot as pltimg plt.imread(test.jpg)# plt.rcParams[font.sans-serif] [SimHei] # 用来正常显示中文标签 # plt.rcParams[axes.unicode_minus] False # 用来正常显示负号 #有中文出现的情况&#xf…

宏auto关键字(C++基础)

宏 宏可以实现对语句的同义替换&#xff0c;简单来说就是预处理阶段、编译前的替换&#xff08;包括符号&#xff0c;变量等&#xff09;。 #define LOG(x) std::cout << x << std::endl; LOG("hello") 可以正常使用。 下面通过上图中借用不同开发模…

YOLOv8改进 | 独家创新篇 | 利用DCNv3集合DLKA形成全新的注意力机制(全网独家创新)

一、本文介绍 本文给大家带来的机制是由我独家创新结合Deformable Large Kernel Attention (D-LKA) 注意力机制和DCNv3可变形卷积的全新注意力机制模块(算是二次创新),D-LKA的基本原理是结合了大卷积核和可变形卷积的注意力机制,通过采用大卷积核来模拟类似自我关注的感受…

MySQL学习笔记(一)数据库事务隔离级别与多版本并发控制(MVCC)

一、数据库事务隔离级别 数据库事务的隔离级别有4种&#xff0c;由低到高分别为Read uncommitted &#xff08;读未提交&#xff09;、Read committed&#xff08;读提交&#xff09; 、Repeatable read&#xff08;可重复读&#xff09; 、Serializable &#xff08;串行化&a…

OpenAI劲敌吹新风! Claude 3正式发布,Claude3使用指南

Claude 3是什么&#xff1f; 是Anthropic 实验室近期推出的 Claude 3 大规模语言模型&#xff08;Large Language Model&#xff0c;LLM&#xff09;系列&#xff0c;代表了人工智能技术的一个显著飞跃。 该系列包括三个不同定位的子模型&#xff1a;Claude 3 Haiku、Claude 3…

06 - ip route和route -n的区别

1 ip route和route -n的区别 ip route 和 route -n 都是用于查看和管理Linux系统路由表的命令。但下面是它们的区别&#xff1a; ip route&#xff1a;是Linux系统中的现代工具&#xff0c;它属于iproute2套件&#xff1b;它提供了更多的选项&#xff0c;可以更精确地控制路由表…

弱电综合布线:连接现代生活的纽带

在当今信息化快速发展的时代&#xff0c;弱电网络布线作为信息传输的重要基础设施&#xff0c;其作用日益凸显。它不仅保障了数据的高效流通&#xff0c;还确保了通信的稳定性。从商业大厦到教育机构&#xff0c;从政府机关到医院急救中心&#xff0c;再到我们居住的社区&#…

EIP-1559

EIP EIP是以太坊改进提案&#xff08;Ethereum Improvement Proposal&#xff09;的缩写。它是一种标准化的提案制度&#xff0c;用于描述和讨论对以太坊区块链网络的改进和升级。EIP的目的是提供一个开放的、透明的过程&#xff0c;让社区成员、开发者和其他利益相关者能够共同…

短视频矩阵系统----矩阵系统源码搭建(技术门槛?)

短视频矩阵是什么意思&#xff1f;短视频矩阵的含义可以理解为全方位的短视频账号&#xff0c;通过不同的账号实现全方位的品牌展示。实际上是指一个短视频账号&#xff0c;通过不同的链接实现品牌展示&#xff0c;在不同的粉丝流量账号中互相转发同一个品牌&#xff0c;在主账…

pytorch CV入门3-预训练模型与迁移学习.md

专栏链接&#xff1a;https://blog.csdn.net/qq_33345365/category_12578430.html 初次编辑&#xff1a;2024/3/7&#xff1b;最后编辑&#xff1a;2024/3/8 参考网站-微软教程&#xff1a;https://learn.microsoft.com/en-us/training/modules/intro-computer-vision-pytorc…

外包干了8天,技术退步明显。。。。。

先说一下自己的情况&#xff0c;本科生&#xff0c;19年通过校招进入杭州某软件公司&#xff0c;干了接近3年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试…

能源大数据采集,为您提供专业数据采集服务

随着经济的不断发展&#xff0c;能源产业也逐渐成为国民经济的支柱产业之一。而对于能源行业来说&#xff0c;数据采集是一项至关重要的工作。以往&#xff0c;能源企业采集数据主要依靠人工收集、整理&#xff0c;但是这种方式不仅效率低下&#xff0c;而且容易出现数据不准确…

Python测试框架Pytest的基础入门

Pytest简介 Pytest is a mature full-featured Python testing tool that helps you write better programs.The pytest framework makes it easy to write small tests, yet scales to support complex functional testing for applications and libraries. 通过官方网站介绍…

SpringBoot 手写 Starter

1.介绍 SpringBoot中的starter是一种非常重要的机制&#xff0c;能够抛弃以前繁杂的配置&#xff0c;将其统一集成进starter&#xff0c;应用者只需要在maven中引入starter依赖&#xff0c;SpringBoot就能自动扫描到要加载的信息并启动相应的默认配置。starter让我们摆脱了各种…

九型人格测试,8号领袖型人格的职业分析

8号人格&#xff0c;也叫领袖型人格&#xff0c;在九型人格中间&#xff0c;是一种天生领导的存在。他们生性开朗&#xff0c;能够和其他人建立良好的关系&#xff0c;为人不拘小节&#xff0c;遇强则强&#xff0c;坚守心中的理想和正义。不喜欢被人控制&#xff0c;喜欢自己当…

Wireshark——获取捕获流量的前N个数据包

1、问题 使用Wireshark捕获了大量的消息&#xff0c;但是只想要前面一部分。 2、方法 使用Wireshark捕获了近18w条消息&#xff0c;但只需要前5w条。 选择文件&#xff0c;导出特定分组。 输入需要保存的消息范围。如&#xff1a;1-50000。 保存即可。