BT

如何利用碎片时间提升技术认知与能力? 点击获取答案

解耦应用与依赖注入框架

| 作者 Mike Bria 关注 0 他的粉丝 ,译者 张龙 关注 14 他的粉丝 发布于 2010年1月22日. 估计阅读时间: 6 分钟 | CNUTCon 了解国内外一线大厂50+智能运维最新实践案例。

由于SOA、TDD等众多因素的影响,依赖注入已成为近年来广为接受的软件开发方法,随之而来的则是依赖注入框架的大量应用(尤其是最近Java EE 6所包含的依赖注入)。Bob Martin则通过实例演示了一种解耦应用代码与依赖注入框架的方法。

Dependency Injection Inversion这篇文章中,Bob大叔总结到:

...我可不想让依赖注入框架的代码散布在我的应用当中。相反,我想解耦框架与我自己编写的代码。

为了阐述这个问题,Martin给出了一个示例,该示例围绕着BillingService类的创建展开,该类的构造方法定义了如下依赖:

public class BillingService {
	...
	BillingService(CreditCardProcessor processor, TransactionLog transactionLog) {
		this.processor = processor;
		this.transactionLog = transactionLog;
	}
	...
}

首先,Martin使用Guice(Google推出的依赖注入框架)创建BillingService类的实例:

public static void main(String[] args) {
	Injector injector = Guice.createInjector(new BillingModule());
	BillingService billingService = injector.getInstance(BillingService.class);
	billingService.processCharge(2034, "Bob");
}

在解释完该段代码的一些细节后,Martin给出这样一个事实:我们不得不显式地让Guice通过injector来创建BillingService的实例。这么做的结果就是:使用BillingService的代码不再依赖BillingService的依赖了(这很好),但却依赖Guice了。

是不是有利就有弊呢?Martin的回答是肯定的:

依赖注入不过是依赖倒置(Dependency Inversion)的一个特殊情况而已。我认为依赖倒置非常重要,因此打算转换对Guice的依赖。我可不想让那么多的Guice依赖搞乱了我的代码。

之后,他向我们展示了如何通过工厂对象来控制并降低应用对DI框架的依赖:

public static void main(String[] args) {
	Injector injector = Guice.createInjector(new BillingModule());
	BillingService.factory = new BillingServiceFactory(injector);
}
...
// Deep in the bowels of my system.
BillingService billingService = BillingService.factory.make();
billingService.processCharge(2034, "Bob");

为什么这种方式比较好呢,Martin说到:

我喜欢这么做,因为现在所有的Guice代码都放在了同一个地方,而非散布在应用的各个角落,这是通过工厂实现的。不仅如此,如果将Guice替换成其他DI框架,我会明确知道需要修改哪些类以及如何修改他们。通过这种方式达到了应用与Guice解耦的目的。

要澄清的一点是:在所有的示例中,BillingService本身都坚持着依赖注入原则,而BillingService到底使用的是依赖(CreditCardProcessor及TransactionLog)的何种实现其本身是不得而知的。为了说明这一点,他在文章的最后通过JUnit对BillingService进行了测试,在测试中仅仅使用了简单的TransactionLog和CreditCardProcessor实例对其进行注入,最后无论应用使用何种依赖注入手段,测试结果都是正确的。

Gary Bernhardt就这个测试也给出了一篇文章,提到在Java这种静态类型语言中这么做需要慎之又慎,但对于Python之类的动态语言就无所谓了。

你是否使用依赖注入呢?是否使用DI框架呢?不管答案如何,以上这种做法是否与你产生共鸣了呢?

查看英文原文:Decoupling Your Application From Your Dependency Injection Framework

有InfoQ读者对Bob大叔的这种做法提出了不同的观点:

来自InfoQ英文站的读者monser corp说到:

我想知道Bob大叔在过去几年编了多少代码?

恩,这是一种典型的”顾问式“做法:了解一项新技术,拿起来玩弄一下,然后发表在博客上来满足自己的虚荣心。在我做过的很多项目(使用了Spring)中,只有一个类会依赖于Spring:获取Context并启动应用。99.99%的代码都不知道Spring或Guice是啥(如果不是使用了Spring的某些库的话,IoC容器与Log4J根本就没啥区别)。如果真的想将代码与容器解耦,你需要重新考虑设计与实现,而非使用另一个工厂。

作为读者的您有何高见呢?这里,译者推荐您先去阅读Bob大叔的原文,然后将您的高见发表在这里。

评价本文

专业度
风格

您好,朋友!

您需要 注册一个InfoQ账号 或者 才能进行评论。在您完成注册后还需要进行一些设置。

获得来自InfoQ的更多体验。

告诉我们您的想法

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

Spring by 马 运超

的确只有一个类依赖于Spring,但所有的配置文件都依赖于Spring

Re: Spring by wang metis

IOC容器不过是大的统一的工厂,方便我们管理依赖罢了,有必要这么纠结吗

Re: Spring by manqing li

大部分的应用设计者都不会愚蠢到在各个地方直接使用IOC框架的API,工厂应该是个最常用的解决方法。
但是对IOC框架的依赖不会体现在API上,而是在配置上,我们的配置文件会直接和我们的应用的IOC框架紧耦合,注解的使用可以去掉配置文件的依赖但是产生代码级的依赖
如果能对注解进行继承问题就解决了,我们期待新的JSR299和JSR330会提供更好的解决方案

Re: Spring by Ma Karl

这有什么关系呢?
每一种IOC容器都需要一套自己的注入规则。

好事 by jiang rosen

java ee原生支持DI,不是挺好么。

Re: Spring by Wong Peter

不管怎么做,你总是需要去依赖某样东西。Bob这里的做法只是为了不让Guice代码散落在所有角落。

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

6 讨论

登陆InfoQ,与你最关心的话题互动。


找回密码....

Follow

关注你最喜爱的话题和作者

快速浏览网站内你所感兴趣话题的精选内容。

Like

内容自由定制

选择想要阅读的主题和喜爱的作者定制自己的新闻源。

Notifications

获取更新

设置通知机制以获取内容更新对您而言是否重要

BT