深度理解依赖注入(Dependence Injection)
发布时间:2020-12-14 05:27:44 所属栏目:百科 来源:网络整理
导读:深度理解依赖注入(Dependence Injection) 前面的话:提到依赖注入,大家都会想到老马那篇经典的文章。其实,本文就是相当于对那篇文章的解读。所以,如果您对原文已经有了非常深刻的理解,完全不需要再看此文;但是,如果您和笔者一样,以前曾经看过,似乎
深度理解依赖注入(Dependence Injection)
前面的话:提到依赖注入,大家都会想到老马那篇经典的文章。其实,本文就是相当于对那篇文章的解读。所以,如果您对原文已经有了非常深刻的理解,完全不需要再看此文;但是,如果您和笔者一样,以前曾经看过,似乎看懂了,但似乎又没抓到什么要领,不妨看看笔者这个解读,也许对您理解原文有一定帮助。
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 从上面代码的注释中可以看到,MovieLister和ColonDelimitedMovieFinder(这可以使任意一个实现了MovieFinder接口的类型)之间存在强耦合关系,如下图所示: 这使得MovieList很难作为一个成熟的组件去发布,因为在不同的应用环境中(包括同一套软件系统被不同用户使用的时候),它所要依赖的电影查找器可能是千差万别的。所以,为了能实现真正的基于组件的开发,必须有一种机制能同时满足下面两个要求: (1)解除MovieList对具体MoveFinder类型的强依赖(编译期依赖)。 (2)在运行的时候为MovieList提供正确的MovieFinder类型的实例。 ?? 换句话说,就是在运行的时候才产生MovieList和MovieFinder之间的依赖关系(把这种依赖关系在一个合适的时候“注入”运行时),这恐怕就是Dependency Injection这个术语的由来。再换句话说,我们提到过解除强依赖,这并不是说MovieList和MovieFinder之间的依赖关系不存在了,事实上MovieList无论如何也需要某类MovieFinder提供的服务,我们只是把这种依赖的建立时间推后了,从编译器推迟到运行时了。 ?? 依赖关系在OO程序中是广泛存在的,只要A类型中用到了B类型实例,A就依赖于B。前面笔者谈到的内容是把概念抽象到了服务使用者和服务提供者的角度,这也符合现在SOA的设计思路。从另一种抽象方式上来看,可以把MovieList看成我们要构建的主系统,而MovieFinder是系统中的plugin,主系统并不强依赖于任何一个插件,但一旦插件被加载,主系统就应该可以准确调用适当插件的功能。 ?? 其实不管是面向服务的编程模式,还是基于插件的框架式编程,为了实现松耦合(服务调用者和提供者之间的or框架和插件之间的),都需要在必要的位置实现 面向接口编程,在此基础之上,还应该有一种方便的机制实现 具体类型之间的运行时绑定,这就是DI所要解决的问题。 2.DI的实现方式 ?? 和上面的图1对应的是,如果我们的系统实现了依赖注入,组件间的依赖关系就变成了图2: 说白了,就是要提供一个容器,由容器来完成(1)具体ServiceProvider的创建(2)ServiceUser和ServiceProvider的运行时绑定。下面我们就依次来看一下三种典型的依赖注入方式的实现。特别要说明的是,要理解依赖注入的机制,关键是理解容器的实现方式。本文后面给出的容器参考实现,均为 黄忠成老师的代码,笔者仅在其中加上了一些关键注释而已。 2.1 Constructor Injection(构造器注入) 我们可以看到,在整个依赖注入的数据结构中,涉及到的重要的类型就是ServiceUser,ServiceProvider和Assembler三者,而这里所说的构造器,指的是 ServiceUser的构造器。也就是说,在构造ServiceUser实例的时候,才把真正的ServiceProvider传给他:
1
接下来我们看看Assembler应该如何构建:
2 3 4 5 6 7 8 9
1
需要在强调一下的是,依赖并未消失,只是延后到了容器被构建的时刻。所以正如图2中您已经看到的,容器本身(
更准确的说,是一个容器运行实例的构建过程)对ServiceUser和ServiceProvoder都是存在依赖关系的。所以,在这样的体系结构里,ServiceUser、ServiceProvider和容器都是稳定的,互相之间也没有任何依赖关系;所有的依赖关系、所有的变化都被封装进了容器实例的创建过程里,符合我们对服务应用的理解。而且,在实际开发中我们一般会采用配置文件来辅助容器实例的创建,将这种变化性排斥到编译期之外。
2 3 4 5 6 7 8 9 10 11 12 13 ?? 即使还没给出后面的代码,你也一定猜得到,这个container类一定有一个GetInstance(Type t)这样的方法,这个方法会为我们返回一个已经注入完毕的MovieLister。 一个简单的应用如下:
1
上面最关键的就是对pico.getComponentInstance的调用。Assembler会在这个时候调用MovieLister的构造器,构造器的参数就是当时通过pico.registerComponentImplementation(MovieFinder.class,ColonMovieFinder.class,finderParams)设置进去的实际的ServiceProvider--ColonMovieFinder。下面请看这个容器的参考代码:
2 3 4 5 6 7 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |