浅谈依赖注入

di

区分概念

知道与一个概念的相关概念的很重要的,有对比才能理解,才不会混为一谈:

  • 控制反转
  • 依赖注入
  • 依赖注入模式

关于这几个概念的定义,在后面出现不理解时还要回过头来查看,心里需要清楚这里有三种相关概念。

控制反转 (Inversion Of Control, IOC)

控制反转 是面向对象编程中的一种设计思想,可以用来减低计算机代码之间的耦合度。其中最常见的实现方式叫做依赖注入(Dependency Injection, DI), 还有一种叫”依赖查找”(Dependency Lookup)。 当一个类的实例需要另一个类的实例协助时,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。当创建被调用者的工作不再由调用者来完成时,就叫控制反转。

控制反转有多么的重要呢?参考下面这段话:

框架与库的区别

控制反转如此之重要,上述观点总结如下:

  • 库和框架的区别就在于是否使用控制反转;
  • 库只复用了算法和数据结果,而框架还复用了控制流逻辑,这也是控制反转带来的。

依赖注入(Dependency Injection,DI)

什么是依赖注入,就是:对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中

依赖注入其实非常简单,就是在最原始的代码里,被依赖的对象是在调用者内部创建的,如下:

1
2
3
4
5
6
7
8
9
class DBHandler(object):
pass

class Cache(object):
_handler = None
def __init__(self):
self._handler = DBHandler()

c = Cache()

这样非常强耦合,不好。依赖注入就是把这个依赖放在外面,创建好了之后再传递给构造函数,如下:

1
2
3
4
5
6
7
8
9
10
class DBHandler(object):
pass

class Cache(object):
_handler = None
def __init__(self, handler):
self._handler = handler

h = DBHandler()
c = Cache(h)

这样的好处在于,以后如果继承了一种新的 DBHandler 的子类,也可以不用修改 class Cache 的代码。意味着降低了 Cache 类对外部的依赖程度,以前 Cache 依赖于 DBHandler 的具体实现,而现在只依赖于 DBHandler 这个抽象接口,系统耦合度降低了。

但是,这会不会太简单了!感觉依赖注入这个词很高端的样子,就这么简单吗?是的,的确这么简单。不过,我们通常说的依赖注入,更多的是指一种运用了依赖注入思想的设计模式。

依赖注入模式

依赖注入这个词常常不加区别的指一种设计模式,使用一个依赖注入管理容器进行依赖的管理。创建被调用者的实例的工作由IOC容器自动完成,然后自动注入调用者,因此也称为依赖注入。

举个简单的例子:
(1)原始社会里,几乎没有社会分工。需要斧子的人(调用者)只能自己去磨一把斧子(被调用者)。此时:使用斧子的人与斧子的具体实现强耦合。
(2)进入工业社会,工厂出现。斧子不再由普通人完成,而在工厂里被生产出来,此时需要斧子的人(调用者)找到工厂,购买斧子,无须关心斧子的制造过程。此时:使用斧子的人不再关心斧子的具体实现,只关心斧子的接口,耦合度降低了。
(3)进入“按需分配”社会,需要斧子的人不需要找到工厂,坐在家里发出一个简单指令:“需要斧子”,斧子就自然出现在他面前。此时:IOC容器减少了使用者找工厂的步骤,更加方便了。

但是有一点必须清楚,IOC容器的确更方便了,也仅仅是更方便了。系统的耦合度并没降低,斧子的使用者依然依赖于斧子的接口,只是不用显式的去创建了,而是由IOC容器隐式自动完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#! /usr/bin/python
# encoding: utf-8
# Author: chuanqi.tan # gmail.com
# http://tanchuanqi.com


class AutoDiInterface(object):
'''自动注入接口'''
_di = None

def setDi(self, di):
self._di = di


class DiContainer(object):
'''IOC容器'''
_servers = {}

def set(self, key, server):
self._servers[key] = server

def get(self, key):
if key in self._servers:
server = self._servers[key]()
if isinstance(server, AutoDiInterface):
server.setDi(self) # 自动设置依赖管理容器

return server

raise Exception('server not found')


class NormalHandler(AutoDiInterface):
_memory = {}

def set(self, key, value):
self._memory[key] = value

def get(self, key):
return self._memory[key]


class ReverseHandler(NormalHandler):
def set(self, key, value):
self._memory[key] = value[::-1]


class Cache(AutoDiInterface):
_handler = None
_handler_name = ''

def __init__(self, handler_name):
self._handler_name = handler_name

def getHandler(self):
if not self._handler:
self._handler = self._di.get(self._handler_name) # cache 说,我需要一个这样的handler

return self._handler

def set(self, key, value):
self.getHandler().set(key, value)

def get(self, key):
return self.getHandler().get(key)


app = DiContainer()
app.set('normal_handler', lambda: NormalHandler())
app.set('reverse_handler', lambda: ReverseHandler())
app.set('cache', lambda: Cache('normal_handler'))
app.set('reverse_cache', lambda: Cache('reverse_handler'))


c = app.get('cache') # 说一声我需要一个 cache
c.set('k', 'hello world')
print c.get('k')

c = app.get('reverse_cache') # 说一声我需要一个 reverse_cache
c.set('k', 'hello world')
print c.get('k')

输出如下:

hello world
dlrow olleh
0%