您的位置:首页 > 其它

周末随笔-聊一聊IOC和DI

2017-09-09 15:06 176 查看
首先,本文探讨的目的是如何让系统的扩展性变高,以实现降低迭代成本。追求开发速度的完全不必理会。

IoC 控制反转

IoC(Inverse of Control),控制反转,但这个说法不够直白,在实际生产中,用控制转移来描述反而地道一点。今天不抠字眼,对象的控制权转交给第三方,往往就是:某一接口具体实现类的选择控制权从调用类中移除,转交给第三方决定。 但还是很难理解。

DI 依赖注入

随后Martin Fowler提出了DI(Dependency Injection)依赖注入的概念用以代替IoC,即:

让调用类对某一接口实现类的依赖关系由第三方(容器或协作类)注入,以移除调用类对某一接口实现类的依赖。

“依赖注入”显然比“控制反转”更好理解。

看一个例子

interface I {
void foo();

class A implements I{
public A() {
//...
}
//...
}

class B {
I i;
public B() {
i = new A();
//...
}
public void bar() {
}
}


可以看到,类B的功能依赖于A,而且这里使用了组合。

当然,使用组合要比让B继承A并进行扩展好得多,这不是今天要讨论的。

这个例子的问题在于:B依赖的A由B构建。看起来好像没什么问题,但是:

B负责了A的创建,不符合SRP 单一职责原则。

当A业务扩展时,可能构造函数 发生改变,这样就要修改B的构造函数,不符合OCP 开闭原则

如果不面向接口编程的话,这个代码会更加糟糕,为了后面方便,这里已经提出了接口I(以减少本文的注意力分散)

倘若业务发展,B需要有子类时,很大可能是重构,(因为很大可能不符合里氏代换原则,就不往下讨论了)。

这就是大家说的耦合变高了。

按照IoC或者DI来构建代码,可以减少这样的问题。

依赖注入的方式

构造函数注入

setter方法注入

接口注入

注解注入

构造函数注入

class B {
I i;
public B(I i) {
this.i = i;
}
}


这样选择控制权已经不再属于B(IoC想描述的),一种注入形式实现了依赖对象的获得(DI想描述的)。

setter方法注入

class B {
I i;
public B() {
}

public void setI(I i) {
this.i = i;
}
}


接口注入

定义注入接口:

上面接口名字定的不好,暂不修改了

interface InjectDependencyI {
void setI(I i);
}

class B implements InjectDependencyI {
I i;
public B() {
}

public void setI(I i) {
this.i = i;
}
}


和setter注入差别不大,但是当接口I的客户有多个而且没有继承关系或者兄弟关系时,使用接口注入效果会更好一点,代码整体的可读性会好一点。

注解注入

显然,这里存在一个IoC容器,按照规则帮我们进行一定的自动化处理,减少了手动注入的工作,而这些规则是使用注解描述的。

Android中使用较多的是Dagger/Dagger2.本篇就先不扩展了。

写在最后

为什么在Android客户端工作中我们比较难体会到IoC/DI的作用呢?

往往实际情况是:更多的注意力在界面、交互上,业务部分没有那么庞大,某一种业务的客户可能只有一个,即使当前的设计耦合高了不利于扩展,但根本没有矛盾爆发的机会。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ioc DI