title: 深入理解IoC/DI设计原则
date: 2018-6-2 17:38:30
tags:
- IoC/DI
- 依赖注入
- 控制反转
categories: - 设计原则
IoC/DI概念
IoC(Inversion of Control)控制反转
在Java开发过程中IoC意味着将你设计好的类交给系统去控制,而不是在你的类内部控制。IoC是一种让服务消费者不直接依赖于服务提供者的组件设计方式,也是一种减少类与类之间依赖的设计原则。
DI(Dependency Injection)依赖注入
即组件之间的依赖关系由容器在运行期决定,形象的来说即由容器动态的将某种依赖关系注入到组件之中。
依赖注入的目标并非为软件系统带来更多的功能,而是为了提升组件重用的概率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可以指定目标需要的资源,从而完成自身的业务逻辑,而不用关心具体的资源来自何处,由谁实现。
IoC/DI理解
控制反转
- 谁控制谁?
- 正向:指的是当前程序的方向。例如A对象要使用B对象,现在是在A里面直接创建B的实例,然后再调用。简而言之就是程序需要什么就由程序主动去获取需要的资源,这个方向称为正向。
- IoC/DI容器控制应用程序
- 控制什么?
- IoC/DI容器控制对象本身的创建、实例化
- IoC/DI容器控制对象之间的依赖关系
- 为何叫反转(对应于正向)?
因为现在应用程序不能主动去获取外部资源,而是被动等待IoC/DI容器给它注入所需资源,所以将这种行为称之为反转。 - 哪些方面反转了?
- 创建对象
- 程序获取资源的方式
- 为何需要反转?
- 引入容器之后体系更为松散,管理更加有序
- 类之间真正实现了松散耦合,使得开发、测试、维护升级都变得容易
IoC/DI并没有帮助我们实现任何的业务功能,原本该由应用实现的功能还是由应用自身完成。
依赖
- 什么是依赖?
- 名词:依赖关系
- 动词:依赖的动作(注入)
- 谁依赖于谁?
应用程序依赖于IoC/DI容器 - 为什么需要依赖?
因为反转了之后应用程序依赖的资源都在IoC/DI容器里面 - 依赖什么东西?
应用程序依赖于IoC/DI容器,依赖IoC/DI容器为它注入所需要的资源(比如:依赖关系)。
注入
- 谁注入谁?
IoC/DI容器注入应用程序 - 注入什么东西?
注入应用程序需要的外部资源,比如依赖关系 - 为何要注入?
因为程序要正常运行需要这些资源
依赖注入和控制反转是同一个概念吗?
不是同一个概念,但是对同一件事情不对角度的描述。所以其实描述的是同一件事情,只是以不同的角度在思考。控制反转是从IoC/DI容器的角度来思考,而依赖注入是从应用程序的角度在思考。
参与者都有哪些?
参与者主要是应用程序和Spring
IoC/DI是什么?
- IoC:就是使用IoC/DI容器反过来控制应用程序所需要的外部资源,这样一种程序的开发思想。
- DI:就是应用程序依赖IoC/DI容器来注入所需要的外部资源,这样一种程序的开发思想。
- 能做什么?
松散对象的耦合 - 怎么做?
- 可以自己实现
- 使用已经实现好的IoC/DI容器,例如:Spring
- 用在什么地方?
- 凡是程序里面需要使用到外部资源的情况,都可以考虑使用IoC/DI容器。
- 比如工厂类现在就可以不用再使用了
- 外部资源
对一个类来讲所谓外部资源,就是指在自己类的内部不能得到或实现的东西,比如:在类里面要读取一份配置文件,那么这份配置文件就是这个类的外部资源。
IoC/DI基本思想
Java基本阶段
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p9WZwhjJ-1675526824308)(/images/Ke360G00.png)]
在C类里面需要使用接口A,最原始的写法是:
A a = new A1();
//或者
A a = new A2();
然后通过 a
来调用接口中的方法。对于C来说是主动实例化对象,直接获取它所依赖的资源。
- 这种方法好吗?有什么问题?
更换实现需要重新编译源代码,很难更换实现,难于测试,耦合实例生产者和实例消费者。
Factory阶段
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bZhY0htE-1675526824308)(/images/pKe0k0m00.png)]
上述方法进化到使用工厂模式(Factory),在C类里面需要使用接口A,应用工厂模式的写法是:
A a = Factory.createA();
然后通过a来调用接口的方法。对于C来说是被动实例化对象,通过工厂间接获取依赖,但对工厂类来说是主动的。
- 这种方法好吗?有什么问题?
更换实现需要重新编译源代码,很难更换实现,难于测试。
Factory + XML + 反射阶段
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QlwuAjG0-1675526824309)(/images/sfU2j170000.png)]
继续进化到使用工厂模式加配置文件,在C类里面需要使用接口A,应用工厂模式的写法是:
A a = Factory.createA();
然后通过 a
来调用接口的方法,在工厂类里使用配置文件来决定最终实例化的具体类。对C类来说是被动创建对象,间接获取依赖,对工厂来说也是被动的。
- 这种方法好吗?有什么问题?
在这种情况下还会遇到很多的问题,比如:如何实例化带参数的类,如何在对调用的方法传递值等等。
IoC/DI阶段
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xfQxmf72-1675526824310)(/images/phiinguuPie0foNe.png)]
继续进化就到IoC/DI的层次了,在这种设计里面出现了IoC/DI容器,容器在对IoC/DI涉及的元素进行整体控制,并提供更多更好的通用服务。此时在C类里面需要使用接口A,以Spring为例:
A a = BeanFactory.getBean("XX");
然后通过 a
来调用接口的方法。
此时由容器来创建和装配对象,并管理对象生命周期。对于应用程序而言就是被动实例化和被动接受依赖了。
基本思想
- 把程序之间的依赖关系去掉
- 把程序对象设置到IoC/DI容器的配置中作为Bean
- 由IoC/DI容器来管理Bean的创建、实例化
- 由IoC/DI容器来把Bean之间的关系注入到需要这些关系的对象里面。
简而言之就是对象之间的依赖全部去掉,然后由IoC/DI容器来管理对象和对象之间的依赖关系。
功能
实现了对象之间的松散耦合
IoC容器
简单的理解就是:实现IoC思想并提供对象创建、装配以及对象生命周期管理的软件。
IoC理解
- 应用程序无需主动new对象,而是描述对象应该如何被创建,IoC容器帮你创建,即被动实例化。
- 应用程序不需要主动装配对象之间的依赖关系,而是描述需要哪个服务,IoC容器会帮你装配(即负责将它们关联在一起),被动接受装配。
- 主动变被动,体现好莱坞法则:别打电话给我们,我们会打给你。
- 体现迪米特法则(最少知识原则):应用程序不知道依赖的具体实现,只知道要提供某类服务的对象(面向接口编程):并松散耦合,一个对象应当对其他对象有尽可能少的了解,不和陌生人(实现)说话。
- 是一种让服务消费者不直接依赖于服务提供者的组件设计方式,是一种减少类与类之间依赖的设计原则。
思考
- IoC/DI等同于工厂吗?
不等同。IoC/DI思想源于工厂而高于工厂,它是工厂+容器。 - IoC/DI跟以前的方式有什么不一样?
- 不需要再使用工厂了
- 在实际使用各个框架时会有不同
领会:主从换位的思想
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R3q9Yqf5-1675526824311)(/images/eishoh8feinguKei.png)]
使用IoC/DI容器开发需要改变的思路
- 应用程序不主动创建对象,但要描述创建它们的方式。
- 在应用程序代码中不直接进行服务的装配,但要描述哪一个组件需要哪一项服务,由容器负责将这些装配在一起。
- 也就是说:所有的组件都是被动的,组件初始化和装配都由容器负责,应用程序只是在获取相应的组件后实现应用的功能即可。
小结
IoC/DI是思想不是纯技术实现。IoC只是控制权的转移到框架,所以不能因为实现了IoC就叫IoC容器,而一般除了实现了IoC外,还具有DI功能的才叫IoC容器,因为容器除了要负责创建并装配组件关系,还需要管理组件生命周期。