解读 Java 经典巨著《Effective Java》90条编程法则,第5条:优先考虑依赖注入来引用资源

【前言】欢迎订阅【解读《Effective Java》】系列专栏

《Effective Java》是 Java 开发领域的经典著作,作者 Joshua Bloch 以丰富的经验和深入的知识,全面探讨了 Java 编程中的最佳实践。这本书被公认为 Java 开发者的必读经典,对提升编码技巧和代码质量具有重要意义。

在《Effective Java》中,Bloch 总结了 Java 编程的精髓,涵盖了从对象创建和销毁,到类和接口设计,再到泛型、枚举和并发编程的方方面面。每一条法则不仅是对具体问题的解决方案,更是经验的凝练,旨在帮助开发者编写更高效、可维护且健壮的代码。

为了帮助大家更好地理解和应用这些编程原则,我们推出了【解读《Effective Java》】系列专栏。在这里,我们将逐条解读《Effective Java》第三版中的90条编程法则。每篇文章将详细总结和分析法则,结合实际代码示例和应用场景,帮助您将理论知识转化为实际编程能力。

适合人群:

  • Java 初学者:掌握 Java 编程的核心原则和最佳实践。
  • 中级开发者:获取深入见解和高级技巧,提升编码能力。
  • 资深工程师:优化和重构现有代码,提升开发效率。

敬请关注我们的专栏,与我们一起深入探索 Java 编程的精髓,提升您的编码技能。感谢您的订阅与支持,我们期待与您共同踏上这段充满知识和启发的编程之旅!

感谢您的订阅:解读《Effective Java》

博主公众号

在这里插入图片描述


优先考虑依赖注入来引用资源

Joshua Bloch 在《Effective Java》中提出的第5条编程法则“优先考虑依赖注入来引用资源”着重强调了依赖注入(DI)的重要性,该法则主要关注如何有效地管理类和资源之间的依赖关系。

什么是依赖注入?

依赖注入(Dependency Injection,简称 DI)是一种设计模式,用于管理对象之间的依赖关系。它的核心思想是将对象的创建和所依赖的对象分离,即对象不再负责创建或管理依赖对象,而是将这些职责交给外部系统或容器。

  • 依赖:一个类(依赖者)所需要的其他对象(依赖对象)来完成它的功能。
  • 注入:将依赖对象提供给依赖者的过程,而不是让依赖者自己创建这些对象。

由于对象的依赖是通过注入的方式提供的,而不是对象内部自行创建和管理,组件之间的耦合度降低。这使得组件更容易被替换或修改,而不会影响到其他部分。

当然!让我们通过一个具体的场景来对比依赖注入和内部创建的方式。在这个例子中,我们将使用一个简单的消息发送系统来说明这两种方法的不同。

内部创建 VS 依赖注入

假设我们需要设计一个消息发送系统,支持不同的方式用于发送通知,如电子邮件、短信等。

设计方案:为了确保系统具有良好的扩展性和可维护性,我们可以遵循面向对象设计原则,面向接口编程,我们可以定义一个通知服务接口来定义所有的通知服务需要实现的基本功能;还需要向外提供一个负责统一管理通知服务的类,以便调用方可以使用不同的通知服务发送通知。

公共的通知服务接口定义

通知服务接口 NotificationService

通知服务接口(NotificationService)用于定义发送通知的标准接口,任何具体的通知服务类都需要实现这个接口。

public interface NotificationService {/*** 发送通知的方法* @param message 通知内容*/void sendNotification(String message);
}
电子邮件通知 EmailService

电子邮件通知(EmailService)实现 NotificationService 接口,负责发送电子邮件通知的具体逻辑。

public class EmailService implements NotificationService {@Overridepublic void sendNotification(String message) {// 电子邮件发送逻辑System.out.println("Sending email: " + message);}
}
短信通知 SMSService

短信通知(SMSService)实现 NotificationService 接口,负责发送短信通知的具体逻辑。

public class SMSService implements NotificationService {public void sendNotification(String message) {System.out.println("Sending SMS: " + message);}
}

内部创建方式

NotificationManager 用于负责管理通知的发送。

// 通知管理器
public class NotificationManager {private NotificationService notificationService;public NotificationManager() {// 内部创建 EmailService 服务实例this.notificationService = new EmailService();}public void notifyUser(String message) {notificationService.sendNotification(message);}
}

在这种方式中,NotificationManager 直接创建和管理 EmailSender 的实例,使用 EmailSender 邮件来提供通知服务。

// 使用示例
public class Main {public static void main(String[] args) {NotificationManager manager = new NotificationManager();manager.notifyUser("Hello via default service!");}
}

这种方法虽然实现简单,但是 NotificationManager 和具体的通知服务实现紧密耦合,不利于代码的扩展和维护,不能轻松更改或切换 NotificationService 的实现。如果需要使用短信通知的方式,就需要修改 NotificationManager 的内部实现。

依赖注入方式

在依赖注入方式中,NotificationManager 通过构造函数或其他注入方式接受 NotificationService 的实现。这样可以在创建 NotificationManager 实例时动态提供所需的服务实现。

public class NotificationManager {private final NotificationService notificationService;public NotificationManager(NotificationService notificationService) {this.notificationService = notificationService;}public void notifyUser(String message) {notificationService.sendNotification(message);}
}

在这种方式下,NotificationManager 不直接创建 NotificationService 实例,而是通过构造函数注入或其他注入方式来获取 NotificationService 实例。

// 使用示例
public class Main {public static void main(String[] args) {// 使用电子邮件通知服务NotificationService emailService = new EmailService();NotificationManager emailManager = new NotificationManager(emailService);emailManager.notifyUser("Welcome to our service!");// 使用短信通知服务NotificationService smsService = new SMSService();NotificationManager smsManager = new NotificationManager(smsService);smsManager.notifyUser("Your verification code is 1234.");}
}

这种方法遵循面向对象设计中的依赖倒置原则,使得高层模块(NotificationManager)不依赖于低层模块(具体的通知服务实现)。使得 NotificationManager 与具体的 NotificationService 实现解耦,有利于代码的维护和扩展,可以轻松地更改或替换 NotificationService 的实现,而无需修改 NotificationManager 的代码。

依赖注入的最佳实践

优先考虑依赖注入来引用资源 是提升代码质量和可维护性的有效策略。通过依赖注入,可以使代码更加模块化,从而简化测试和维护过程。然而,使用依赖注入也会有一些风险,例如出现类之间循环依赖、过度依赖的问题。

因此使用依赖注入的最佳实践是通过框架来实现依赖注入,如 Spring。Spring 通过自动装配、灵活的配置管理和生命周期管理,简化了依赖关系的管理。通过注解或 XML 配置来定义和管理 bean,确保对象的创建、初始化和销毁都由框架处理,减少了手动配置的工作量。此外,Spring 的模块化设计使得应用程序的管理和扩展更加方便,使得依赖注入的实现更加高效和可靠。

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

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

相关文章

STM32巡回研讨会总结(2024)

前言 本次ST公司可以说是推出了7大方面,几乎可以说是覆盖到了目前生活中的方方面面,下面总结下我的感受。无线类 支持多种调制模式(LoRa、(G)FSK、(G)MSK 和 BPSK)满足工业和消费物联网 (IoT) 中各种低功耗广域网 (LPWAN) 无线应…

MelosBoom:解锁数据价值的新纪元

在当今的数字时代,数据被誉为“新的石油”,但用户在传统的Web2环境中,往往无法真正享受到自己贡献数据的价值。大型互联网公司通过集中化的系统和算法,垄断了数据的使用权,并从中获取巨大的商业利益,而数据…

计算机三级网络技术总结(一)

RPR环中每一个节点都执行SRP公平算法IEEE 802.11a和g将传输速率提高到54Mbps一个BGP发言人与其他自治系统中的BGP发言人要交换路由信息就要先建立TCP连接在一个区域内的路由器数一般不超过200个进入接口配置模式&#xff1a;Router(config)#interface <接口名> 封装ppp协…

Qt 实现自定义截图工具

目录 Qt 实现自定义截图工具实现效果图PrintScreen 类介绍PrintScreen 类的主要特性 逐步实现第一步&#xff1a;类定义第二步&#xff1a;初始化截图窗口第三步&#xff1a;处理鼠标事件第四步&#xff1a;计算截图区域第五步&#xff1a;捕获和保存图像 完整代码PrintScreen.…

WLAN实验简述

一&#xff1a;配置生产AP1上级接入层交换机LSW3 sys [Huawei]sysname LSW3 [LSW3]undo info-center enable [LSW3]vlan batch 10 100 [LSW3]int g0/0/2 [LSW3-GigabitEthernet0/0/2]port link-type trunk [LSW3-GigabitEthernet0/0/2]port trunk allow-pass vlan 10 100 [LSW…

Java企业面试题3

1. break和continue的作用(智*图) break&#xff1a;用于完全退出一个循环&#xff08;如 for, while&#xff09;或一个 switch 语句。当在循环体内遇到 break 语句时&#xff0c;程序会立即跳出当前循环体&#xff0c;继续执行循环之后的代码。continue&#xff1a;用于跳过…

STM32 的 CAN 通讯全攻略

目录 一、CAN 通讯概述 二、 CAN 通讯原理 1.ISO11898 标准下的物理层特征 2.CAN 协议的帧类型 3. 总线仲裁介绍 4.位时序 5.STM32 CAN 控制器简介 6.标识符筛选器 三、软件设计 1.发送流程 1.1初始化 CAN 控制器 1.2准备发送数据 1.3 将数据填充到发送缓冲区 1.4…

Vue.js入门系列(二十九):深入理解编程式路由导航、路由组件缓存与路由守卫

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1…

pikachu下

CSRF(跨站请求伪造) CSRF(get) url变成了这样了&#xff0c;我们就可以新开个页面直接拿url去修改密码 http://pikachu-master/vul/csrf/csrfget/csrf_get_login.php?username1&password2&submitLogin CSRF(post&#xff09; 这里只是请求的方式不同&#xff0c;…

简洁明了!中缀表达式转为后缀表达式规则及代码

简单来说&#xff0c;就是弄两个栈&#xff0c;判断执行&#xff1a; 上代码&#xff1a; #include<iostream> #include<stack> #include<cstring> using namespace std; stack<char>s1,s2; char now; int main(){string c;cin>>c;for(int i0;…

Linux 开发工具(vim、gcc/g++、make/Makefile)+【小程序:进度条】-- 详解

目录 一、Linux软件包管理器 - yum&#xff08;ubuntu用apt代替yum&#xff09;1、Linux下安装软件的方式2、认识 yum3、查找软件包4、安装软件5、如何实现本地机器和云服务器之间的文件互传 二、Linux编辑器 - vim1、vim 的基本概念2、vim 下各模式的切换3、vim 命令模式各命令…

形式向好、成本较低、可拓展性较高的名厨亮灶开源了

简介 AI视频监控平台, 是一款功能强大且简单易用的实时算法视频监控系统。愿景在最底层打通各大芯片厂商相互间的壁垒&#xff0c;省去繁琐重复的适配流程&#xff0c;实现芯片、算法、应用的全流程组合&#xff0c;减少企业级应用约 95%的开发成本&#xff0c;在强大视频算法加…

电子连接器温升仿真教程 一

电子连接器温升是指电子连接器的所有端子在施加额定电载荷的情况下,经过一段时间后,达成热平衡,连接器局部温度不再继续升高,此时规定测试点的温度与测试环境温度的差值。连接器的温升规格值因其应用环境不同,而不同。工业应用,且不与人体接触的电子连接器一般允许温升会…

mybatis 查询Not Found TableInfoCache

近期在工程迁移中遇到一个mybatis查询的问题&#xff0c;检查代码没有问题&#xff0c;但是报Not Found TableInfoCache 解决过程 是不是数据库对应表错误或者实体类指定的表名错误 查看配置文件链接的数据源是否正确TableName中指定的表名然后去数据库看一下是否存在 如果…

【C++】模板进阶:深入解析模板特化

C语法相关知识点可以通过点击以下链接进行学习一起加油&#xff01;命名空间缺省参数与函数重载C相关特性类和对象-上篇类和对象-中篇类和对象-下篇日期类C/C内存管理模板初阶String使用String模拟实现Vector使用及其模拟实现List使用及其模拟实现容器适配器Stack与Queue 本章将…

SQL进阶技巧:如何将字符串数组清洗为简单map结构? | translate + regexp_replace方法

目录 0 场景描述 1 数据准备 2 问题分析 2.1 方法1 特征法-通用解法 2.2 方法2枚举法(不通用) 3 小结 ~~END~~ 如果觉得本文对你有帮助,那么不妨也可以选择去看看我的博客专栏 ,部分内容如下: 数字化建设通关指南专栏原价99,现在活动价29.9,按照阶梯式增长,直到恢…

Linux命令分享 四 (ubuntu 16.04)(vi操作文件)

1、su 切换用户 su - 用户名 切换到该用户并将目录切换至该用户的主目录 **注意该语句执行后需要输入密码&#xff0c;输入密码时终端不回显&#xff08;不会显示你输入的密码&#xff09;&#xff0c;输完直接回车即可 su 用户名 切换用户但不切换目录 su - root su root **注…

c++20 std::format 格式化说明

在标头<format>定义 ()功能很强大&#xff0c;它把字符串当成一个模板&#xff0c;通过传入的参数进行格式化&#xff0c;并且使用大括号‘{}’作为特殊字符代替‘%’。 1、基本用法 &#xff08;1&#xff09;不带编号&#xff0c;即“{}”&#xff08;2&#xff09;带…

2024年【浙江省安全员-C证】考试试卷及浙江省安全员-C证模拟考试题库

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 浙江省安全员-C证考试试卷是安全生产模拟考试一点通总题库中生成的一套浙江省安全员-C证模拟考试题库&#xff0c;安全生产模拟考试一点通上浙江省安全员-C证作业手机同步练习。2024年【浙江省安全员-C证】考试试卷及…

arcgisPro修改要素XY容差

1、在arcgisPro中XY容差的默认值为1个毫米&#xff0c;及0.001米。为了更精细的数据&#xff0c;需要提高这个精度&#xff0c;如何提高呢&#xff1f; 2、如果直接在数据库下新建要素类&#xff0c;容差只能调至0.0002米。所以&#xff0c;需要在数据库下新建要素数据集。 3…