Dependency Injection 经常简称为:DI。html
它是实现控制反转(Inversion of Control – IoC)的一个模式。web
fowler 大大大神 “几十年”前的经典文章 https://www.martinfowler.com/articles/injection.html 说的很清楚。面试
“几十年”以来,相信你们都早已学会了 大大大神 的教典。数据库
咱们简单回忆一下对应内容,以便咱们能够顺利进入后续章节:徒手撸个小DI。框架
文章内容大体是这样:函数
首先举例:性能
public interface MovieFinder { List findAll(); } class MovieLister { private MovieFinder finder; public MovieLister() { finder = new ColonDelimitedMovieFinder("movies1.txt"); } public Movie[] moviesDirectedBy(String arg) { List allMovies = finder.findAll(); for (Iterator it = allMovies.iterator(); it.hasNext();) { Movie movie = (Movie) it.next(); if (!movie.getDirector().equals(arg)) it.remove(); } return (Movie[]) allMovies.toArray(new Movie[allMovies.size()]); }
而后大大大神吐槽了一堆:this
这个实现类的名字就说明:我将要从一个逗号分隔的文本文件中得到影片列表。你没必要操心具体的实现细节,只要设想这样一个实现类就能够了。若是这个类只由我本身使用,一切都没问题。可是,若是个人朋友叹服于这个精彩的功能,也想使用个人程序,那又会怎么样呢?若是他们也把影片清单保存在一个逗号分隔的文本文件中,而且也把这个文件命名为” movie1.txt “,那么一切仍是没问题。若是他们只是给这个文件改更名,我也能够从一个配置文件得到文件名,这也很容易。可是,若是他们用彻底不一样的方式——例如SQL 数据库、XML 文件、web service,或者另外一种格式的文本文件——来存储影片清单呢?在这种状况下,咱们须要用另外一个类来获取数据。因为已经定义了MovieFinder接口,我能够不用修改moviesDirectedBy方法。可是,我仍然须要经过某种途径得到合适的MovieFinder实现类的实例。插件
还有张依赖图设计
MovieLister类既依赖于MovieFinder接口,也依赖于具体的实现类。咱们固然但愿MovieLister类只依赖于接口,但咱们要如何得到一个MovieFinder子类的实例呢?
在Patterns of Enterprise Application Architecture一书中,咱们把这种状况称为插件(plugin):MovieFinder的实现类不是在编译期连入程序之中的,由于我并不知道个人朋友会使用哪一个实现类。咱们但愿MovieLister类可以与MovieFinder的任何实现类配合工做,而且容许在运行期插入具体的实现类,插入动做彻底脱离我(原做者)的控制。这里的问题就是:如何设计这个链接过程,使MovieLister类在不知道实现类细节的前提下与其实例协同工做。
将这个例子推而广之,在一个真实的系统中,咱们可能有数十个服务和组件。在任什么时候候,咱们总能够对使用组件的情形加以抽象,经过接口与具体的组件交流(若是组件并无设计一个接口,也能够经过适配器与之交流)。可是,若是咱们但愿以不一样的方式部署这个系统,就须要用插件机制来处理服务之间的交互过程,这样咱们才可能在不一样的部署方案中使用不一样的实现。因此,如今的核心问题就是:如何将这些插件组合成一个应用程序?这正是新生的轻量级容器所面临的主要问题,而它们解决这个问题的手段无一例外地是控制反转(Inversion of Control)模式。
学术一点就是说 避免类之间强耦合,咱们须要用依赖注入等方式在运行时才创建依赖达到代码松耦合,从而使代码易为维护
戏言就是在说:
因此上述代码中:
我(MovieLister)离不开了 你 (ColonDelimitedMovieFinder("movies1.txt")),
可是咱们男人必须靠本身,至少表面没人看出咱们之间的关系
只有从咱们(MovieLister)身体里面没有了你,才能没人看出咱们之间的关系
当咱们开始干活的时候,咱们再根据咱们的私下关系协调好工做,男女搭配,好好干活。
说到这里, 各位要被面试的同窗记好这些话, 不要被问到依赖注入帮我解决了什么事情的时候, 回一句 咱们不用本身new 对象啦, 这样你们就不会看见面试官无语又懵逼的脸了。
class MovieLister { private IMovieFinder finder; public MovieLister(IMovieFinder finder) { this.finder = finder; } }
class MovieLister { [Inject] public IMovieFinder Finder { get; set;} }
public interface InjectFinder { void injectFinder(MovieFinder finder); } class MovieLister : InjectFinder { private IMovieFinder finder; public void injectFinder(MovieFinder finder) { this.finder = finder; } }
这几种方式之间并无性能或者什么特别的优点,主要是形式上的差别。
具体对比能够参考 http://insights.thoughtworkers.org/injection/