Yesky首页| 产品报价| 行情| 手机 | 数码 | 笔记本 | 台式机 | DIY硬件 | 外设 | 网络 | 数字家庭 | 评测 | 软件 | e时代 | 游戏 | 图片 | 壁纸 | 群乐 | 社区 | 博客 | 下载
软件频道>程序开发>JavaVBVCDelphiC/C++Web开发微软专栏移动数据库程序人生软件工程|产品中心下载什么是软件架构
您现在的位置: 天极网 > 开发频道 > 想快快喝下Google果汁——Guice吗?
全文
群乐:Google

想快快喝下Google果汁——Guice吗?

2008-05-11 09:00 作者: infoQ 王丽娟 出处: 天极网 责任编辑:dizzarz

     如果不用触及大部分源码就能完成修改,那自然再好不过,但当你把大型应用程序翻新为DI的时候,做到这一点着实不易。本文同样适用于将遗留系统转为DI的一般情况,也适用于 Java、.NetPythonRuby等语言,尽管文章是以GoogleJava DI容器(Guice)命名的。这里有点标题党的意思,PicoContainerSpring之类放在这个标题里不如Guice那么合适。

   单件出现

  单件和静态状态已经因问题重重而被指责了多年。

  毫无疑问,使用DI的应用要比东一块西一块的单件应用有更可测、更清晰的架构。尽管在时间压力下,通常情况是用简单的单件来连接各种各样的组件比较容易。但应用中的单件数目,会从开始的一两个变成最后的数十甚至数百个,直至整个团队表明代码是不可维护的。老板不允许重新编写应用却期望有DI的特性,如此看来,朝着DI的方向重构当前的代码集是务实的做法。

  

  一旦你决定改变方向、想进入DI的天堂,你就必须要知道该走哪条路。当然,你可以停止功能开发、缺陷修复,一门心思地去实现DI,但这并不是正确的做法。相反,你希望能同时交付新的功能,而且经验表明,在一两周之内使用起来Guice(或Spring等)的承诺往往会在好几个月之后才能兑现。即便仅在一个分支上进行DI重构的工作,这跟仍然在进行功能开发和缺陷修复的其他分支也是背道而驰的,当DI重构完成时就会有合并错误的风险。

  而且从哪里开始着手也不清晰。你是先在main()方法中插入一个空的DI容器吗?还是从Web层开始,从那里接近DI?抑或是先用单件查找的结果填充DI容器,写上TODO注释表示以后再完善吗?无论哪种方法都是杂乱并令人生厌的。

  用“Service Locator”作为到依赖注入的垫脚石

  Martin Fowler在2003年写了一篇关于依赖注入的权威文章。当时DI领域尚不成熟,Martin论述了服务定位器是一个比较有价值的替代选择。当然,对不同的人来说,服务定位器意味着不同的东西,我们先假设它表示一个带有像下面一样方法的类(更确切地说它本身是一个单件)……

  

以下是引用片段:
public Object getService(String serviceName) { // etc}或public T getService(Class serviceType); // etc}

  其思想是在程序的入口(main方法?),把与服务名对应的实例填充进服务定位器,单元测试的时候可以混合使用真实或模拟的实例来填充。凡是在遗留代码集中出现单件查找的地方,稍加修改即可换用服务定位器来查找同样的组件。这种方法只适用于“作用域范围为整个应用(application scoped)”的服务/组件。现代Web框架还有会话、请求等不同作用域范围的组件,框架会在进入作用域的时候相应地处理依赖注入。既然你正在朝着DI 方向前进,那么显然你的应用中还没使用任何现代的Web框架,那就只好将就一下了。服务定位器需要在启动的时候填充……

  

以下是引用片段:
public void setService(String serviceName, Object implementation) { // etc}

  为了安全,你需要用一种机制来锁定服务定位器并使之只读。应该在main方法结束的地方、调用任何启动生命周期方法之前调用lock()方法锁定服务定位器,这样可以有效地防止误用。

  把服务定位器放进来仅仅是一个开始。接下来逐步用服务定位器的getService()方法替换掉单件的 getInstance()方法。实际上这些单件现在已经变成了“被管理的单一实例”。你可能会发现这是分离组件的接口和实现的好时机。分离组件的接口和实现的一个原因是为了使模拟更容易(具体请Google搜索EasyMock、JMock或Mockito),因为很显然,你可以趁机提高应用的测试覆盖率。

  当你把单件消灭完毕之后,应该重新检查一遍所有调用服务定位器的代码片段。每当需要组件/服务的时候,都会出现 getService调用,甚至可能在一个类中出现多次(而且每次超出作用域范围的时候组件都被垃圾回收)。如果出现这种情况,那么聪明的做法是只在构造函数中调用一次getService,然后将结果储存为成员变量。

 

共2页。 1 2 :
网友关注
最新上市
编辑推荐
欢迎订阅天极网RSS聚合资讯:http://www.yesky.com/index.xml