15

控制反转(IOC)和依赖注入(DI)

 4 years ago
source link: https://sikasjc.github.io/2019/11/12/IOC-DI/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

IoC — Inversion of Control,控制反转

DI — Dependency Injection,依赖注入

IOC,是一种设计原则:

通过将组件(Components)的设置和使用分开,来降低类别或模组之间的耦合度(即,解耦)

Martin Fowler,因认为“IoC”的意义易使人困惑,于2000年初,与多位IoC提倡者,给实际实践IOC的方式起一个更具体的名称— — “Dependency Injection (依赖注入)”。

控制反转IOC

控制反转,指实例依赖成员的『控制流程(Control Flow)』,由主动控制变成被动控制,因此被称为控制反转。

小明有一台iPhone手机,他写了个程序来控制这个iphone手机来抢红包,自动发短信,自动抢购等等,这些过程可以用如下方式简单描述:

  • 打开iPhone,打开微信,进入群聊,抢红包
  • 打开iPhone,打开短信,编辑短信内容,选择收件人,发送短信
  • 打开iPhone,打开小米商城,选择需要抢购的商品,等待到达抢购时间,开始抢购
  • ······

大家很容易就能看出来问题,这些过程和iPhone耦合的很紧密,如果小明需要换手机了,那么那需要在每个过程中都将iPhone换为新手机。这时,就需要控制反转,我们不再主动去获得手机,而是被动的接收一个手机。

这样这些流程就可以更改为:

  • 接收手机
    • 打开手机,打开微信,进入群聊,抢红包
    • 打开手机,打开短信,编辑短信内容,选择收件人,发送短信
    • 打开手机,打开小米商城,选择需要抢购的商品,等待到达抢购时间,开始抢购

这样,我们发现,过程和具体的一个手机(例如,iPhone)耦合度降低了,可以很简单的更改手机的实例。

另一个例子

我们去网咖打游戏,是不可能自己带游戏去网咖的,而是网咖都把这些游戏提供好了。

需要的游戏,不用自己下载,而是网咖提供给你。

==>

需要的依赖实例,不用主动(Active)建立,而是被动(Passive)接收。

这就是控制反转(Inversion of Control)

简单来说,A依赖B,但A无法控制B的创建和销毁,仅使用B,那么B的控制权交给C(A以外)处理

  • 第一个例子,小明是A,依赖手机B,C可以是其他任何给小明手机的人(可能有点牵强)
  • 第二个例子,我们是A,依赖游戏B,C则是网吧

依赖注入DI

刚才那两个例子中,小明需要手机,我们需要玩游戏,那么有哪些方法可以让我们被动接收这些东西呢?假设小明是A,手机是B

  • 通过A的接口,把B传入
  • 通过A的构造,把B传入
  • 通过设置A的属性,把B传入

这个过程就是依赖注入,即A啥都没干,就可以直接使用B,比如我们啥也不用干,就可以直接玩游戏,真香。

也可以说,依赖注入是实现控制反转的一种方式,它们之间有着密切的联系

依赖倒置原则DIP

实际上,之前所说的控制反转是非常像:

依赖倒置原则(Dependency Inversion Principle, DIP)

这个原则是指,高阶模块不要依赖低阶模块,而是依赖抽象。

  • 我们喜欢吃油泼面
  • 我们也喜欢吃鱼香肉丝盖饭
  • 我们还喜欢吃江边城外烤全鱼
  • ······

那么,这些如果写到代码里,将会是非常紧密的耦合,假如有一天我们不喜欢吃油泼面,或者无法吃到油泼面,则代码非常多的地方需要更改

因此可以进一步抽象,

  • 我们喜欢吃面,我们→面←油泼面
  • 我们也喜欢吃盖饭,我们→盖饭←鱼香肉丝盖饭
  • 我们还喜欢吃烤鱼,我们→烤鱼←江边城外烤全鱼
  • ······

还可以更进一步的抽象

  • 我们喜欢吃食物,我们→食物←面,盖饭,烤鱼······

我们真正所需要的、依赖的,其实不是实际的类别与物件,而是它所拥有的功能。 其实这就是依赖倒置原则DIP (Dependency Inversion Principle)

进一步地:

  • 高阶模块不应该依赖于低阶模块,两者都该依赖抽象
  • 抽象不应该依赖于具体实现方式
  • 而是具体实作方式应该依赖抽象

实际上,面向接口编程就是一个具体地实践方法

IoC 容器(IoC Container)

IoC容器(又称:服务容器Service Container),随着依赖注入的频繁使用,来实现控制反转,会有很多重复代码,甚至随着技术的发展,有更多新的实现方法和方案,那么有人就把这些实现控制反转的代码打包成组件或框架,来避免人们重复造轮子。

广义上来说, IoC容器,就是有进行依赖注入的地方,你随便写一个类别,透过它将所需元件注入给高阶模组,便可说是容器。

但现在所说的『容器』,往往是泛指那些强大『框架』的容器

  • 动态生成实例,create,resolve,make等,为当前实例注入依赖
  • 注册依赖关系,利用配置或其他方式查找依赖关系,例如bind,register,link
  • ······

Reference


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK