当前位置: 首页 > news >正文

IOC控制反转的解耦(相比于直接new对象的正向控制)

直接new对象,是你在主动寻找和创建你需要的“零件”;而 IoC 则是你告诉一个“管家”你需要什么“零件”,由“管家”帮你找到或创建好,然后递给你。

这个“管家”就是 IoC 容器(比如 Spring 容器),而“递给你”的这个动作,最常见的实现方式就是依赖注入(Dependency Injection, DI)

下面我们通过一个例子来解释清楚。

场景:UserService 需要使用 UserDao 来操作数据库

假设我们有一个 UserService 类,它负责用户相关的业务逻辑,比如用户注册。它需要依赖一个 UserDao 类来与数据库进行交互。UserDao 是一个接口,它有一个具体的实现类 UserDaoImpl

Java

// 数据访问层接口
public interface UserDao {void addUser(User user);
}// 接口的实现类
public class UserDaoImpl implements UserDao {@Overridepublic void addUser(User user) {System.out.println("数据库:添加用户 " + user.getName());}
}

1. 直接 new 一个对象(正向控制)

在这种方式下,UserService 类需要自己负责创建它所依赖的 UserDaoImpl 对象。

代码示例:

Java

public class UserService {// UserService 内部自己创建了 UserDaoImpl 实例private UserDao userDao = new UserDaoImpl();public void register(User user) {// ... 其他业务逻辑,如检查用户名密码等userDao.addUser(user);}
}

分析这种方式的问题(为什么会耦合):

  1. 高度耦合UserService 和具体的实现类 UserDaoImpl 紧紧地“绑死”在了一起。UserService 的代码里写死了 new UserDaoImpl()
  2. 难以更换实现:如果有一天,我们不想用 UserDaoImpl 了,想换成一个新的、性能更好的 UserDaoMongoImpl (用 MongoDB 数据库),我们必须修改 UserService 的源代码,把 new UserDaoImpl() 改成 new UserDaoMongoImpl()。如果有一百个类都依赖 UserDaoImpl,你就得改一百次。
  3. 难以测试:当我们要对 UserService 进行单元测试时,我们希望用一个“假的” UserDao (Mock 对象)来模拟数据库操作,而不是真的去连接数据库。但由于 UserDaoImpl 是在 UserService 内部 new 出来的,我们根本没有办法在外部替换它,导致单元测试非常困难。

总结:控制权在 UserService 手中,它主动去创建依赖,导致了它和依赖的实现类之间强烈的耦合关系。

2. 使用 IoC / DI (控制反转)

在 IoC 模式下,UserService 不再自己创建依赖,它只负责“声明”自己需要一个 UserDao 类型的依赖。对象的创建和管理权被交给了 IoC 容器(例如 Spring)。

代码示例:

Java

// 使用 Spring 框架的注解来示意
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service // 告诉 Spring:这个类交给你管理
public class UserService {// 依赖一个接口,而不是具体的实现类private final UserDao userDao;// 通过构造函数注入依赖@Autowiredpublic UserService(UserDao userDao) {this.userDao = userDao;}public void register(User user) {// ... 其他业务逻辑userDao.addUser(user);}
}// UserDaoImpl 也需要被 Spring 管理
@Repository // 告诉 Spring:这个数据访问层类也交给你管理
public class UserDaoImpl implements UserDao {@Overridepublic void addUser(User user) {System.out.println("数据库:添加用户 " + user.getName());}
}

分析这种方式的好处(为什么会解耦):

  1. 实现了解耦UserService 只依赖于抽象的 UserDao 接口,它根本不知道、也不关心最终传进来的是 UserDaoImpl 还是 UserDaoMongoImpl。它只知道“我需要一个能操作用户的 DAO,是谁不重要,能用就行”。

  2. 轻松更换实现:如果我们想把 UserDaoImpl 换成 UserDaoMongoImpl,我们完全不需要修改 UserService 的代码。只需要在 Spring 的配置中告诉它:“以后凡是需要 UserDao 的地方,都给他 UserDaoMongoImpl 的实例”。

  3. 非常容易测试:在进行单元测试时,我们可以非常轻松地手动创建一个 MockUserDao 对象,然后通过 UserService 的构造函数传进去,从而实现对 UserService 的隔离测试。

    Java

    // 测试代码
    @Test
    void testRegister() {// 创建一个假的 DaoUserDao mockDao = new MockUserDao(); // 手动注入这个假的 Dao 来创建 UserServiceUserService userService = new UserService(mockDao);userService.register(new User("test"));// ... 断言 mockDao 的方法是否被正确调用
    }
    

总结UserService 失去了创建依赖的控制权,这个控制权被“反转”给了 IoC 容器。容器负责创建好 UserDaoImpl 的实例,然后通过依赖注入(这里是构造函数注入)的方式“塞”给 UserServiceUserService 从一个主动的创建者,变成了一个被动的接收者。

http://www.hskmm.com/?act=detail&tid=9709

相关文章:

  • 墨者学院 浏览器信息伪造
  • AT_arc156_c [ARC156C] Tree and LCS
  • 完整教程:ARM指令集总结
  • 封神台 第二章:遇到阻难!绕过WAF过滤
  • 封神台 第三章:为了更多的权限!留言板!
  • 第一篇
  • C#开发ONVIF客户端与RTSP播放库指南
  • 一行命令查看docker所有网络 + 子网
  • ECT-OS-JiuHuaShan框架元推理,是马克思主义与我思故我在的完美统一,是超越自我
  • vulnhub Beelzebub
  • Salesforce 管理员:是终点,还是跳板?
  • 记一次内务培训
  • CH584 CH585 复位状态寄存器
  • 2025CCPC邀请赛(南昌)VP(A,B,C,D,G,H,K,L)
  • 不用手也能玩手机?多代理协作框架让 APP 自动执行任务
  • MATLAB实现单帧图像超分辨率重建
  • 绕过安全控制安装第三方软件
  • 详细介绍:认知语义学意象图式对人工智能自然语言处理中隐喻分析的影响与启示
  • 完整教程:LeetCode 刷题【81. 搜索旋转排序数组 II、82. 删除排序链表中的重复元素 II、83. 删除排序链表中的重复元素】
  • 软件体系结构概论 1章
  • vLLM常用参数解释
  • 故障处理:ORA-00600 2252故障处理
  • Android 平台 MAUI 应用更新服务
  • SQL脚本:查询指定SQL的统计信息(cursor,awr)
  • 逆向分析之if语句与循环语句的分析
  • 读书笔记:索引组织表(IOT):让数据库查询飞起来的黑科技
  • 损失曲线出现先下降后上升
  • 【IEEE出版】第六届信息科学与并行、分布式处理国际学术会议(ISPDS 2025)
  • *控制线设计
  • 索引器的用法