欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  后端开发

如何理解IoC(Inversion of Control)?

程序员文章站 2024-01-29 10:07:34
...
RT,如何理解IoC(Inversion of Control)?如果能写个demo解释最好了。

回复内容:

RT,如何理解IoC(Inversion of Control)?如果能写个demo解释最好了。

我试着搜了一下,中文资料基本都把IoC(控制翻转)和DI(依赖注入)混为一谈,就连*上的第一位答案也犯了同样的问题(幸好下面有高票的反对评论,否则我的世界观都快坏掉了)

控制翻转是代码复用的一种模式(注意不是设计模式)

一般(非IoC)的复用,通常是用户代码调用组件(任意形式的被复用的代码,本答案中统称为组件)。也就是用户代码解决“Why it works, What to do”,组件解决“How to do”,逻辑的入口是用户代码,

而控制翻转则是组件来调用用户代码,也就是组件解决“Why it works, When to do”,用户代码解决“What to do”,逻辑的入口是组件

下面是除了DI之外的控制翻转的例子

  • 接口/虚函数 组件调用接口/虚函数,具体由用户代码实现
  • 发布订阅(事件)模式 组件触发事件,用户代码订阅事件
  • 回调 用户代码写回调,组件来调用
  • 几乎所有能被称之为框架的东西 用户代码在规定的地方实现具体业务逻辑,剩下的框架负责

Don't call me, I will call you

正常流程

你去图书馆借书,但是没有。

于是你回家了。

然后你每个天去一趟图书馆,看你的书到了没有。

如果还没有,那么你回家,继续等……

第二天,再去看,……

一直到有了这本书。

Ioc

你去图书馆借书,但是没有。

于是你登记了一下,就回家了。

回到家,刷刷微博,刷刷sf,撸撸代码。

等书到了,管理员通知你。

你去拿书。


Don't call me, I will call you

IoC就是让框架为我们创建些东西。

App::bind('sms', function () {
   // Create a new SMS component.
   $sms = new SMSClient;
   $sms->setUsername('shauna');
   $sms->setPassword('1_5t341_c4t5!');
   // Return the created SMS component.
   return $sms;
});

据Clouser function特征,上面代的代码不会创建$sms对象。
$app->make('sms')的时候,$sms对象才被创建。所谓的反转大概就是按需创建吧。用的时候才触发创建,不是创建好了等着用。

因为程序员本身是农民工的一种,但是又想装的高大上一些,用以自赏,所以创建了这个很NB的术语,装的好像搞原子弹的。不用太在意。

本质是面向抽象编程,IOC描述的是特性,DI描述的具体实现方式,两者有共通的地方。可以这么理解:用DI实现具备IOC特性的框架,最终体现面向抽象编程的理念,增强系统的可扩性。
常见的“多态”就是面向抽象编程,比如:

Person person = new Male();
person.say();
person.run();
person.eat();

现在要把person指定为Female,则只需替换第一行即可:

Person person = new Female();

改动已经很少了,但仍可以做得更彻底一点:可不可以不更改现有代码就完成切换呢?答案就是"DI"。
撇开Spring不谈,咱们自己也完全可以实现一套最简化的类似功能:

Person person = SpringLike.getBeanFromConfig('person');

这行代码的意思就是,Person的实例是根据配置文件产生的,比如:

person=com.xx.Male

具体原理不赘述,大家耳熟能详。
回到上面那个问题,现在要把功能切换到Female,则只需更改配置文件即可:

person=com.xx.Female

将控制权进一步转移到配置文件,因而修改不涉及业务逻辑代码。
可以看出,“DI”是用更彻底的方式使用多态,在面向抽象编程这方面走得更远。"IOC"是面向抽象编程所体现出来的一个特性,本身和“DI”不冲突。

通俗的说,一个项目由各个类组成。一个类在一个项目会被很多地方使用。

如果按照传统的写法就是每个用的地方都需要 new Class()
如果参数很复杂,每个地方都需要new Class(param1, param2, param3, ..)
此时每次修改类的构造函数, 那么每个地方需要跟着修改。工作量大,耦合度高。

但是可以把需要使用的类,初始化一次,放到一个容器中保存起来,其他需要使用的地方,
只需要调用容器的方法Container->getClassINeed()
那么,使用这个类和生成这个类通过中间的容器分开了。
示例可以看Phalcon框架,文档里给的例子,教你一步步搭建自己的容器

一些管理对象生成的设计模式,本身也算一种容器的实现, 例如常见的工厂模式

举个例子,有一天,你去庆丰包子铺吃包子,进门后你首先需要找到点餐台在哪,然后告诉服务员你要什么,然后去领餐口领包子。这是以你为主的流程,一切都在你的掌握之下,但如果你不主动,就没有包子吃。

第二天,你去另一家高大上包子铺吃包子,这里,你只要坐下来说一句我要吃包子,就有服务员给你把包子端上来。这是以餐厅为主的流程,你不需要知道去哪点餐,去哪领包子。

如果把第一个流程作为“正”的话,那第二个流程就是“反”过来了。这就是所谓的IoC。不过,这个名字起的不太容易理解,因为正、反的概念是相对的,你也可以说第二个流程是正的。

把这个概念应用到GUI编程上,就是顺序执行和事件处理的区别。本来是你自己写的程序按顺序执行,例如第一步输入用户名,第二部输入密码,然后执行登录操作。使用了Windows或者其他图形界面框架后,就是由框架来负责运行你的程序,并且在用户输入了内容之后通知你的程序,你再去做处理。相对于顺序执行,事件驱动就是IoC的一种表现形式。

把这个概念应用到依赖管理上,假设你的程序需要引用另一个对象,本来你是需要在代码里创建这个对象,应用IoC模式后,你的程序不再去主动创建第三方对象,而是运行在一个容器或者框架里,例如Spring,然后等着Spring把你需要的对象创建好传递进来。这就是依赖注入(DI),IoC的另一种表现形式。

由于三言两语说不清楚,我写了一篇文章在这里,http://segmentfault.com/blog/zhaoyi/1190000002411255

建议看看spring框架的原理,就是依赖注册

正常的写法,都是上层调用底层封装好的方法,由底层提供接口,所谓的控制反转指的是,底层的执行逻辑可以由上层来决定,写的是什么,底层就执行什么,它的好处在于灵活、易扩展,就像@bigxu 写的代码一样。

顧名思義吧,你寫出的程式是在操縱些什麼,比如說,給一些數據搬家,但是怎麼來指揮你的程式開始操控它要操控的對象呢?那麼你有要寫一些程式來做指(調)揮(用)你的程式,你定義一個規則,或者遵循一套規則,以便讓人來操控自身,其實這就是控制反轉,即,你是要把控制權交給別人的,仔細想想,其實這種反轉無處不在,更重要的是,怎麼讓別人來調用你的程式?需要約定接口,所以IoC也引導你更多的從面向接口方面考慮編程問題

IoC,控制反转,把对XX的控制交给别人。
spring的DI就是你把new对象的工作交给spring。

参考Spring Ioc容器
推荐阅读的文章
Inversion of Control Containers and the Dependency Injection pattern
InversionOfControl
自己动手写一个IoC工具

ioc 通俗一点的说,你把他想成一个大工厂,你问他要什么object,他就给你什么object,就是这么简单
他就像一个全局变量,在每个object里面的都可以访问到
你问他要什么object,他就给你什么object

ioc主要作用是用来解析类的实例,为什么要解析?而不是直接获得?因为类有其他类的依赖关系。ioc容器可以把多层级的依赖关系全部解析出来。

如果你看的是Laravel框架,可以看看:Service Container(IOC容器)

依赖注入和控制反转都是为了达到相同的解耦效果,Ioc确实不够开门见山,因此业界称进行了广泛的讨论,最终软件界的泰斗级人物Martin Fowler提出了DI的概念用以替代Ioc,即让调用类对某一接口实现类的依赖关系都第三方(容器或协作类)注入,以移除调用类对某一接口实现类的依赖。

依赖注入是控制反转的一种实现方式。

上一篇: Oracle数据安全面面观

下一篇: