【前言】欢迎订阅【品读《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 的模块化设计使得应用程序的管理和扩展更加方便,使得依赖注入的实现更加高效和可靠。