1. 过度使用构造函数注入导致构造函数爆炸
- 实际项目中的影响:
- 可读性差:构造函数参数过多,难以直观理解每个参数的作用,增加了代码阅读和维护成本。例如,一个类
UserService
构造函数中有10个参数,调用者很难迅速明白这些参数分别对应什么。
- 可测试性降低:在编写单元测试时,为了创建目标类的实例,需要提供大量的模拟依赖对象作为构造函数参数,使得测试代码变得复杂冗长。
- 灵活性受限:当需要新增或移除一个依赖时,构造函数的签名必须修改,这可能导致所有使用该构造函数的地方都需要相应修改,违反了开闭原则。
- 最佳实践规避:
- 方法注入:对于非必需的依赖,可以使用方法注入。例如,
UserService
中有一个偶尔使用的日志记录功能,可通过方法注入:
public class UserService {
private Logger logger;
public void setLogger(Logger logger) {
this.logger = logger;
}
// 业务方法
}
- **依赖对象分组**:将相关的依赖封装成一个对象。例如,将数据库连接相关的配置参数封装到`DatabaseConfig`对象中,构造函数参数就变为`UserService(DatabaseConfig dbConfig, OtherDependency other)`。
- **使用构建器模式**:对于参数较多且有些参数可选的情况,使用构建器模式。例如:
public class UserService {
private final Dependency1 dep1;
private final Dependency2 dep2;
private final Dependency3 dep3;
private UserService(Builder builder) {
this.dep1 = builder.dep1;
this.dep2 = builder.dep2;
this.dep3 = builder.dep3;
}
public static class Builder {
private Dependency1 dep1;
private Dependency2 dep2;
private Dependency3 dep3;
public Builder setDep1(Dependency1 dep1) {
this.dep1 = dep1;
return this;
}
public Builder setDep2(Dependency2 dep2) {
this.dep2 = dep2;
return this;
}
public Builder setDep3(Dependency3 dep3) {
this.dep3 = dep3;
return this;
}
public UserService build() {
return new UserService(this);
}
}
}
2. 使用全局变量替代依赖注入
- 实际项目中的影响:
- 可测试性差:全局变量难以在单元测试中替换为模拟对象,因为全局变量的状态在测试之间可能相互影响,导致测试结果不可靠。例如,一个全局的数据库连接对象,在一个测试中修改了连接状态,可能影响其他测试。
- 耦合度高:代码依赖于全局变量,使得代码的可移植性和复用性降低。如果需要在不同环境中使用该代码,全局变量的配置可能成为问题。
- 并发问题:在多线程环境下,全局变量可能引发线程安全问题。例如,多个线程同时访问和修改全局变量,可能导致数据不一致。
- 最佳实践规避:
- 依赖注入框架:使用Spring、Guice等依赖注入框架,通过配置文件或注解的方式将依赖注入到类中,避免使用全局变量。例如,在Spring中:
@Component
public class UserService {
private final DatabaseConnection dbConnection;
@Autowired
public UserService(DatabaseConnection dbConnection) {
this.dbConnection = dbConnection;
}
}
- **通过方法参数传递依赖**:将所需的依赖作为方法参数传递,使得依赖关系更加明确。例如:
public class UserService {
public void processUser(User user, DatabaseConnection dbConnection) {
// 使用dbConnection处理user
}
}