依赖倒置(DIP)、控制反转(IOC)和依赖注入(DI)

D:Dependency(依赖)

依赖是一种需求关系。比如学生做笔记需要用笔记本,那么就是学生依赖笔记本。

依赖倒置是一种设计原则,控制反转是一种设计模式,而依赖注入是实现控制反转的手段。

 

依赖倒置:

定义:高层模块不应该依赖低层模块,二者都应该依赖其抽象;

           抽象不应该依赖细节,细节应该依赖抽象。

                抽象是指不具体的、笼统的,而细节则是细小的环节或者情节的细微部分,也可以称作是具体的部分。

                例如人是抽象的,中国人、美国人、韩国人则是具体的,也就是细节,而某一国人中的某一个人则是更加具体的细节。

需求来源:

        软件实际开发中,一般将程序代码划分为几个层次模块:业务层模块、逻辑层模块和数据层模块三个模块,其中业务层模块是用于提供服务,即做什么,而业务逻辑层模块则是实现功能的算法,即怎么做,至于数据层模块则是为逻辑层模块提供数据模型。这三个层次模块中,表现层模块依赖业务逻辑层模块或者数据层模块,业务逻辑层模块依赖于数据层模块,即高层模块依赖低层模块。

        现有一个类A需要类B,即类A依赖B,如果此时将类A的依赖改为类C,则需要修改类A部分的代码来实现。而根据上述所言,类A一般是高层模块,负责实现功能或者厘清逻辑,而类B和类C则是低层模块,负责提供高层需要的数据模型,若修改类A代码,则会提高修改代码给程序带来的风险的概率。

(以上学习自:https://blog.csdn.net/briblue/article/details/75093382

        例如需要计算以某物体起始速度为V0,加速度为a,求该物体沿直线行驶T s后的位移,并写出位移公式的推导过程,则计算公式和所需推导应该为:X = V0t + 1/2*a^2,

               若使用了Vt = V0+ at公式求Vt,则只能计算出T s后的速度,即所用公式能发挥的功能够发生了错误,可比作业务层模块出现问题;            ······①;

               若推导位移公式得出结论为X = V0t + a^2,即功能正确,但是V0、t 、 a间关系出现偏差,可比作逻辑层模块出现问题;                           ······②;

               若使推导位移公式得出结论为X = V0t + 1/2*a^2,但计算X时将时间T误写作t1,即提供数据出现偏差,可比作数据层模块出现问题;                ······③。

        ①中情况因为求Vt的公式只能发挥得到Vt的功能,故无论如何改变V0、t 、 a间的逻辑关系或者改变带入数据,都无法得出位移X;②中情况,因为求X的公式能够发挥得到X的功能,即只需要改变V0、t 、 a间的逻辑关系则可以求出位移,而③中只需要改变带入数值即可以得出正确位移。

        从上述例子中可以看出,若业务层模块出现问题,需要修改业务层模块、重构逻辑层模块和数据层模块,逻辑层模块出问题需要重构逻辑,而数据层模块出现问题只需修改数据层模块。也就是说,修改高层模块会给程序带来更大的风险。

实现原理:

        为类A添加依赖接口,而类B和类C实现接口,此时,类A可以通过接口间接和类A或类C连接,而无需修改本身代码。

优势:

      有一天,程序员预备军之一的Cily需要熬夜写学习笔记,因为太困了,所以想要喝拿铁咖啡,此时她需要一个咖啡杯装拿铁咖啡,即咖啡杯依赖拿铁咖啡,暂时不谈Cily依赖咖啡杯,如果有一天Cily又需要熬夜,但是她因为减肥,想要喝茶,而咖啡杯依赖拿铁咖啡,故需要对它进行改造,但杯子是一个整体,想要改造非常不容易。为了方便,Cily只能对杯子引入一个接口,此时无论是咖啡杯、茶杯还是保温杯都是杯子,之后Cily无论想喝茶还是饮料都不需要修改咖啡杯了,直接再买一个杯子就可以了。但是如此一来,Cily就可以不用为了减肥而改喝茶了,因为她为数不多的生活费都拿去买杯子了。谈完咖啡杯依赖拿铁咖啡,再来谈谈Cily依赖咖啡杯,虽然后来Cily不需要一直研究怎么改造咖啡杯了,但是她需要花更多的时间来研究怎么修改自己了,因为杯子可以直接买,但是Cily没办法直接买啊,也就是依赖咖啡杯的Cily想要使用茶杯时只能修改自己。

 

控制反转(该部分整合于:http://www.cnblogs.com/DebugLZQ/archive/2013/06/05/3107957.html):

定义:是面向对象编程中的一种设计模式,可以用来减低计算机代码之间的耦合度。

需求来源:

        面向对象设计的软件系统中,其底层都是由N个对象构成的,各个对象之间通过相互合作,最终实现系统地业务逻辑。因此对象之间的耦合关系是无法避免的,也是必要的,这是协同工作的基础。而现在,伴随着工业级应用的规模越来越庞大,对象之间的依赖关系也越来越复杂,经常会出现对象之间的多重依赖性关系,因此,架构师和设计师对于系统的分析和设计,将面临更大的挑战。对象之间耦合度过高的系统,必然会出现牵一发而动全身的情形。

        例如打开机械式手表的后盖,就会看到与上面类似的情形,各个齿轮分别带动时针、分针和秒针顺时针旋转,从而在表盘上产生正确的时间。下图中描述的就是这样的一个齿轮组,它拥有多个独立的齿轮,这些齿轮相互啮合在一起,协同工作,共同完成某项任务。可以看到,在这样的齿轮组中,如果有一个齿轮出了问题,就可能会影响到整个齿轮组的正常运转。

 

实现原理:

         借助于“第三方”IOC容器实现具有依赖关系的对象之间的解耦。由于引进了中间位置IOC容器,使得A、B、C、D这4个对象没有了耦合关系,齿轮之间的传动全部依靠IOC容器了,全部对象的控制权全部上缴给IOC容器IOC容器,所以,IOC容器成了整个系统的关键核心,它起到了一种类似“粘合剂”的作用,把系统中的所有对象粘合在一起发挥作用,如果没有这个“粘合剂”,对象与对象之间会彼此失去联系,这就是有人把IOC容器比喻成“粘合剂”的由来。如下图:

优势:

      去掉IOC容器后,就是要实现整个系统所需要完成的全部内容。此时,A、B、C、D这4个对象之间已经没有了耦合关系,彼此毫无联系,这样的话,当实现A的时候,根本无须再去考虑B、C和D了,对象之间的依赖关系已经降低到了最低程度。所以,如果真能实现IOC容器,对于系统开发而言,是一件多么美好的事情,参与开发的每一成员只要实现自己的类就可以了,跟别人没有任何关系!

【容器的解释】

         容器也叫做组件容器,是一种特殊的组件,包含了其他的组件。也就是说可以将组件放在组件容器之内,而如果一个组件不是组件容器,则不能包含其他的组件。

         既然组件容器是一种组件,则组件容器也可以放在另一个组件容器中。

         把普通的组件想像成为文具,而把组件容器想像成为抽屉分隔盒。此时,文具可以放在抽屉分隔盒里,抽屉分隔盒和别的文具又可以放在抽屉里。于是 ,抽屉里可以有文具,还可以有其他的抽屉分隔盒。

 

依赖注入:

定义:实现IOC的一种手段

实现原理:当对象无法实例化依赖的时候在外部(即IOC容器中)赋值给他,这个赋值的动作被称之为注入。IOC作为注射器将依赖注入类。即类不实例化依赖,而是通过IOC容器创建依赖后注入其中。(比如对象A需要一个对象B,传统方法需要在对象A中编写代码来获取对象B,而依赖注入则只需要告诉IOC容器,对象A中需要一个对象B,至于如何创建、何时创建对象B,对象A不需要知道。)

        实例化:人是一个对象,但也是一个抽象的概念当赋予他性别、身高、体型等特征后就实例化为一个具体的某一个人。

优势:

      Cily去实验室上自习的时候要用电脑,但是实验室的插座不够。因此Cily需要排插,也就是Cily依赖排插,不过Cily不想再买一个排插并每天带去实验室,于是向实验室的负责老师提出申请,最后实验室老师大手一挥统一购买了排插。为此Cily非常高兴,因为她虽然不知道这个排插是怎么来的,也不知道具体是什么时候送进的实验室,也许是学校拨经费买的,也许是实验室负责老师自己掏腰包买的,又或者是公牛财大气粗赞助的,可那又怎么样呢?至少她得到了排插,而且她那为数不多的生活费也不用除了花在买杯子上还要再买排插了。

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
控制(Inversion of Control,简称IoC)和依赖注入(Dependency Injection,简称DI)是两个相关的概念,用于解耦和管理对象之间的依赖关系。 控制IoC)是一种设计原则,它将对象的创建和管理权从应用程序代码中移到容器中。传统的程序设计中,对象之间的依赖关系由对象自己创建和管理,而在IoC中,容器负责创建和管理对象,并将它们注入到需要它们的地方。 依赖注入DI)是实现IoC的一种方式。它通过将依赖关系作为参数传递给对象,或者通过使用容器来自动注入依赖关系,来实现对象之间的解耦。依赖注入可以通过构造函数注入、属性注入或者接口注入等方式来实现。 下面是一个简单的示例来说明IoCDI的概念: 假设我们有一个UserService类,它依赖于一个UserRepository类来获取用户数据。在传统的程序设计中,UserService需要自己创建UserRepository对象并管理它的生命周期: ```java public class UserService { private UserRepository userRepository; public UserService() { userRepository = new UserRepository(); } public User getUserById(int id) { return userRepository.getUserById(id); } } ``` 而在使用IoCDI的方式下,我们可以将UserRepository的创建和管理交给容器来处理,UserService只需要声明它所依赖的UserRepository,并通过构造函数或者属性注入的方式接收它: ```java public class UserService { private UserRepository userRepository; public UserService(UserRepository userRepository) { this.userRepository = userRepository; } public User getUserById(int id) { return userRepository.getUserById(id); } } ``` 在这个示例中,UserService不再负责创建UserRepository对象,而是通过构造函数接收一个UserRepository对象。这样,我们可以通过容器来创建UserService,并将一个UserRepository对象注入到它中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值