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

news2024/9/21 18:16:22

【前言】欢迎订阅【解读《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 {
    @Override
    public 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.coloradmin.cn/o/2144413.html

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

相关文章

Java 中常用的排序算法

Java 中常用的排序算法有很多,每种算法的时间复杂度和适用场景都不同。以下是几种常见的排序算法及其 Java 实现和讲解: 1. 冒泡排序 (Bubble Sort) 算法思路: 重复地遍历数组,每次比较相邻两个元素。如果前一个比后一个大&…

Web接入Sonic平台之安装

问题及解决方案 1.安装python的airtest-bdd依赖时报错,显示无法编译psutil note: This error originates from a subprocess, and is likely not a problem with pip. ERROR: Failed building wheel for psutil Failed to build psutil ERROR: ERROR: Failed to b…

【2025】基于 SpringBoot 的电影购票系统、电影购票系统、智能电影购票系统、电影购票平台、电影购票管理、微服务电影购票系统(源码+文档+讲解)

博主介绍: ✌我是阿龙,一名专注于Java技术领域的程序员,全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师,我在计算机毕业设计开发方面积累了丰富的经验。同时,我也是掘金、华为云、阿里云、InfoQ等平台…

传输层协议(TCP和UDP)

目录 一、UDP 1、UDPAPI 2、UDPAPI的使用 二、TCP 1、TCPAPI 2、TCP的相关特性 2.1 确认应答 2.2 超时重传 2.3 连接管理(三次握手,四次挥手) 2.4 滑动窗口 2.5 流量控制 2.6 拥塞控制 2.7 延时应答 2.8 捎带应答 2.9 面向字节…

【赵渝强老师】基于ZooKeeper实现Hadoop HA

由于在HA架构中包含的节点比较多,在进行实际部署的时候需要做好集群的规划。图14.9一共使用了4个节点来部署HDFS HA,它们分别是:bigdata112、bigdata113、bigdata114和bigdata115。由于Hadoop默认包含了HDFS和Yarn,因此在部署HDFS…

构建 LLM 应用程序时经常遇到的高级概念的快速指南

使用案例 数据支持的 LLM 应用程序有无数的用例,但大致可以分为四类: 结构化数据提取 Pydantic 提取器允许您指定要从数据中提取的精确数据结构,并使用 LLM 以类型安全的方式填充缺失的部分。这对于从 PDF、网站等非结构化来源中提取结构化…

阿里国际、eBay、乐天等跨境电商如何搭建测评系统给自己店铺测评

要实现自己养号给自己店铺进行测评,确实需要一系列周密的准备和规划,以确保整个过程既稳定安全又有效。以下是详细补充和强化建议: 1. 稳定的测评环境系统 选择高级防关联技术:除了使用国外的服务器、纯净的国外IP和防关联浏览器…

mysql怎样优化count(*) from 表名 where …… or ……这种慢sql

一 问题描述 线上发现一条类似这样的慢sql(查询时长8s): select id,name,(select count(*) from t14 where t14.idt15.id or t14.id2t15.id) as cnt from t15 ; t14的id和id2字段上都有索引,但是因为条件里有or,导致…

Kubernetes调度基础

一、RC 和 RS 1. Replication Controller Replication Controller,简称 RC,复制控制器,可确保Pod 副本数达到期望值,也就是 RC 可确保一个 Pod 总是可用,或一组 Pod 的数量永远处于一个定值。 如果存在的 Pod 大于设…

杭州等保测评揭秘:数据安全如何成为企业的“一道锁”

在数字化时代,数据安全已成为企业和机构面临的重要挑战。杭州作为科技创新的前沿城市,积极推进信息安全建设,其中等保测评(等级保护测评)成为保障数据安全的重要手段。 等保测评是依据《信息安全等级保护管理办法》对…

代码随想录训练营第36天|二维背包

1049. 最后一块石头的重量 II class Solution { public:int lastStoneWeightII(vector<int>& stones) {int sumaccumulate(stones.begin(),stones.end(),0);int targetsum/2;vector<int> dp(target1,0);for(auto& stone: stones){for(int itarget; i>s…

如何快速学习拼音打字?

拼音打字是很多人学会使用电脑或手机的第一步&#xff0c;尤其是对于需要经常输入中文的人来说&#xff0c;熟练掌握拼音打字可以大大提升效率。下面分享一些快速学习拼音打字的方法和技巧&#xff0c;帮助你尽快掌握这项技能。 1. 了解拼音的基本规则 学习拼音打字之前&…

代码随想录算法训练营第五十八天 | 拓扑排序精讲-软件构建

目录 软件构建 思路 拓扑排序的背景 拓扑排序的思路 模拟过程 判断有环 写代码 方法一&#xff1a; 拓扑排序 软件构建 题目链接&#xff1a;卡码网&#xff1a;117. 软件构建 文章讲解&#xff1a;代码随想录 某个大型软件项目的构建系统拥有 N 个文件&#xff0c;文…

Vue|mixin混入

目录 mixin混入什么是mixin代码准备如何混入?全局混合 在 Vue 中&#xff0c;混入&#xff08;Mixins&#xff09;是一种灵活的分发功能&#xff0c;可以将可复用的代码片段和功能注入到多个组件中。简单来说&#xff0c;混入允许你在多个组件之间共享逻辑&#xff0c;而无需重…

QT<24> Qt和windows中获取CPU序列号号以及主板序列号

前言&#xff1a;在qt中获取CPU和主板唯一序列号&#xff0c;可以在程序构造函数中判断是否与windows中一致&#xff0c;不一致可以直接退出程序&#xff0c;防止程序daoyong。 一、获取电脑CPU唯一序列号 QString MainPage::get_cpu() {QString cmd"wmic cpu get proc…

DNS和ICMP

DNS DNS&#xff08;Domain Name System &#xff09; DNS 是一整套从域名映射到 IP 的系统 关于DNS背景 TCP/IP 中使用 IP 地址和端口号来确定网络上的一台主机的一个程序 . 但是 IP 地址不 方便记忆 . 于是人们发明了一种叫主机名的东西 , 是一个字符串 , 并且…

computed计算属性与watch侦听器

1.computed计算属性的写法有两种&#xff0c;一种是只读的&#xff0c;只负责展示&#xff0c;另一种可以进行修改&#xff0c;利用get来获取值&#xff0c;利用set来进行修改 2.watch侦听器的写法也有两种&#xff0c;可以直接写成函数&#xff0c;也可以写成对象&#xff0c;…

浅显易懂的Git教程

Git概述 SVN与Git的对比 SVN&#xff08;Subversion&#xff09; 类型&#xff1a;集中式版本控制系统 工作流程&#xff1a; 从中央服务器下载最新版本到本地。在本地进行开发。提交更改回中央服务器。 优点&#xff1a; 简单易用&#xff0c;适合小型团队。版本历史清…

【源代码+仿真+原理图+技术文档+演示视频+软件】基于物联网的多功能手环设计与实现

摘 要 随着社会进步和生活水平的提高&#xff0c;人们对健康的重视程度不断增加。为了更好地了解自身的身心健康状况&#xff0c;人们对高精密、便携式医疗监控仪器的需求也越来越大。电子信息技术和医学的发展相互促进&#xff0c;二者的结合可以解决人们在健康问题上的困扰…

单通道低压全桥驱动芯片既可应用在电子锁,医疗器械等产品中也能直接Pin to pin贝岭的BL8123

芯片描述 GC8123 是一款低压 5V 全桥驱动芯片&#xff0c;为摄像机、消费类产品、玩具和其他低压或者电池供电的运动控制类应用提供了集成的电机驱动解决方案。GC8123 能提供高达 1.2A 的持续输出电流。可以工作在 1.2~6V 的电源电压上。GC8123 具有 PWM&#xff08;IN/IN&…