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

news2024/11/14 6:22:18

写在开头

记得大学刚毕业那会儿,想学点东西,于是拿出了《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 {

        @Override
        public void cry() {
            System.out.println("呜呜呜~");
        }
    }

    static class Dog implements Animal {

        @Override
        public void cry() {
            System.out.println("汪汪汪~");
        }
    }

    static class Cat implements Animal {

        @Override
        public 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.coloradmin.cn/o/1486889.html

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

相关文章

鸿蒙实战项目开发:【短信服务】

概述 本示例展示了电话服务中发送短信的功能。 样例展示 涉及OpenHarmony技术特性 网络通信 难度级别 中级 基础信息 使用ohos.telephony.sms接口展示了电话服务中发送短信的功能。 效果预览 新建联系人首页短信页 使用说明: 首页点击创建联系人&am…

智能驾驶及相关零部件摄像头毫米波雷达激光雷达和芯片渗透率

一、总体情况 乘联会数据显示,1月1日至1月28日,全国乘用车厂商新能源车批发销量为56.7万辆,同比增长76%,环比下降38%;国内新能源车市场零售销量为59.6万辆,同比增长92%,环比下降24%。 二、销…

如何在手机上中恢复已删除的照片

市场上有大量用于恢复手机已删除照片的应用程序。您可以尝试任何合法的应用程序来恢复意外删除的视频。其中一些应用程序包括 奇客数据恢复、Disk Drill等。 恢复已删除的 Android 照片 如果您不小心从 Android 设备中删除了任何重要视频,无需惊慌。您可以按照这些…

java基础题库详解

目录 1 JDK和JRE有什么区别? 1.1、JRE 1.2、JDK 2、和equals的区别是什么? 3、比较 4、装箱,拆箱 4.1、什么是装箱?什么是拆箱? 4.2、装箱和拆箱的执行过程? 4.3、常见问题 5、hashCode()相同,e…

【ESP32 IDF】key按键与EXTI中断

文章目录 前言一、按键的使用1.1 按键的简介1.2 读取按键的高低电平1.3 读取按键具体代码 二、中断二、EXIT外部中断2.1 EXIT外部中断简介2.2 外部中断基础知识2.3 设置外部中断注册外部中断服务函数设置触发方式添加中断函数 2.4 示例代码 总结 前言 在嵌入式系统开发中&…

【基于Matlab GUI的语音降噪系统设计】

客户不要了,挂网上吧,有需要自行下载~ 赚点辛苦费 ** 功能实现: ** 1、导入音频文件/录入音频,能实现播放功能。 2、对导入/录入的音频信号进行时域和频域分析,并制图。 3、可在导入/录入的音频信号上加入噪声,并能够播…

《异常检测——从经典算法到深度学习》26 Time-LLM:基于大语言模型的时间序列预测

《异常检测——从经典算法到深度学习》 0 概论1 基于隔离森林的异常检测算法 2 基于LOF的异常检测算法3 基于One-Class SVM的异常检测算法4 基于高斯概率密度异常检测算法5 Opprentice——异常检测经典算法最终篇6 基于重构概率的 VAE 异常检测7 基于条件VAE异常检测8 Donut: …

使用ES检索PDF或Word等格式文件方案

#大数据/ES #经验 #方案架构 ES检索PDF/Word等格式文件方案 插件安装 ES有文档预处理插件,但是7.x版本默认发版包不包含这个ingest attachment plugin 。 通过摄取附件插件,Elasticsearch 可以使用 Apache 文本提取库 Tika 提取常见格式的文件附件&a…

go 命令行框架cobra

go 命令行框架cobra go 拉取依赖包go get github.com/spf13/cobra 认识spf13/cobra-cli. cobra 命令行框架在golang中的地位也算得上是大明星级别。像k8s,docker都有使用这个框架构建自己命令行这块的功能. 最最最简单的开始----使用命令行工具cobra-cli来初始化你的demo c…

模糊搜索小案例

C#窗体实现数据录入与模糊搜索小案例 记录一下 主要代码 private void button1_Click(object sender, EventArgs e){string name textBox1.Text;string hometown textBox4.Text;string school textBox6.Text;string sex textBox5.Text;string lat textBox3.Text;string …

STM32 (2)

1.stm32编程模型 将C语言程序烧录到芯片中会存储在单片机的flsah存储器中,给芯片上电后,Flash中的程序会逐条进入到CPU中去执行,进而CPU去控制各种模块(即外设)去实现各种功能。 2.寄存器和寄存器编程 CPU通过控制其…

图像处理ASIC设计方法 笔记6 数据拼接和帧格式校正

第四章大模板卷积ASIC设计方案 P80 实时图SPRM 数据位宽64bit,4个SPRAM,同时得到4行数据 绘制卷积芯片数据路径图,卷积芯片内部模块图 根据这个图,本书后续对各个模块都进行介绍。 P81 第一个模块 图像输入前端FIFO 学习图像处…

【Linux】软件管理yum | 编辑器vim | vim插件安装

目录 1. Linux软件管理yum 1.1 什么是软件包 1.2 查看软件包 1.3 如何安装软件 1.4 如何卸载软件 2. Linux编辑器vim 2.1 vim的基本概念 2.2 vim的基本操作 2.3 vim正常模式命令集 2.4 vim末行模式命令集 2.5 简单vim配置 2.6 插件安装 1. Vim-Plug 3. coc.nvim …

如何保护服务器的安全

互联网的迅速发展,让很多企业都很重视网络技术的使用,但是网络的传播速度比较快,同时容易造成数据、隐私方面的泄露现在每个企业基本有自己的服务器。有几点需要注意,可以参考: 1.基础密码安全 最基本的安全就是密码安…

蜻蜓FM语音下载(mediadown)

一、介绍 蜻蜓FM语音下载(mediadown),能够帮助你下载蜻蜓FM音频节目。如果你是蜻蜓FM会员,它还能帮你下载会员节目。 二、下载地址 本站下载:蜻蜓FM语音下载(mediadown) 百度网盘下载&#…

嵌入式Qt 对话框及其类型 QDialog

一.对话框的概念 对话框是与用户进行简短交互的顶层窗口。 QDialog是Qt中所有对话框窗口的基类。 QDialog继承与QWidfet是一种容器类型的组件。 QDialog的意义: QDialog作为一种专业的交互窗口而存在。 QDialog不能作为子部部件嵌入其他容器中。 QDialog是定制…

js【详解】自动类型转换

运算符 Symbol 数字 会报错 Cannot convert a Symbol value to a number Symbol 字符串 会报错 Cannot convert a Symbol value to a string 存在对象,数组,函数时 对象,数组,函数会先执行其 toString() 方法,…

网络安全概述(一)

目录 资产保护 资产的类型 资产损失类型 资产保护考虑因素 安全强度、安全代价和侵入可能性的关系 信息安全技术发展 单机系统的信息保密阶段 信息保障阶段 信息保障技术框架IATF PDRR模型 网络攻击类型 阻断攻击、截取攻击、篡改攻击、伪造攻击 被动攻击和主动攻…

【设计模式 01】单例模式

单例模式,是一种创建型设计模式,他的核心思想是保证一个类只有一个实例(即,在整个应用程序中,只存在该类的一个实例对象,而不是创建多个相同类型的对象),并提供一个全局访问点来访问…

机器学习|KNN和Kmeans

KNN和Kmeans KNN KNN-K个最近的邻居,而K是可人先预设出来的。 所谓近朱者赤,近墨者黑。 可以选取离当前最近的K个样本来作为辅助判断,因为本样本和最近的K个样本应该是处于一种相似的状态。 以下是一个苹果和梨的识别任务。 图上会出现一个未…