这可能是一个天真的问题.我目前正在学习Spring框架和依赖注入.虽然DI的基本原理很容易掌握,但是为什么需要一个精心设计的框架来实现它并不是很明显.
考虑以下:
public abstract class Saw { public abstract void cut(String wood); } public class HandSaw extends Saw { public void cut(String wood) { // chop it up } } public class ChainSaw extends Saw { public void cut(String wood) { // chop it a lot faster } } public class SawMill { private Saw saw; public void setSaw(Saw saw) { this.saw = saw; } public void run(String wood) { saw.cut("some wood"); } }
然后你可以简单地做:
Saw saw = new HandSaw(); SawMill sawMill = new SawMill(); sawMill.setSaw(saw); sawMill.run();
这相当于:
加:
ApplicationContext context = new ClassPathXmlApplicationContext("sawmill.xml"); SawMill springSawMill = (SawMill)context.getBean("sawMill"); springSawMill.run();
当然,这是一个受到尊重的例子,对于更复杂的对象关系,存储XML文件可能比以编程方式编写文件更有效,但肯定必须有更多的东西呢?
(我知道Spring框架不止于此,但我正在考虑需要一个DI容器.)
在第一个例子中,在中途改变依赖关系也是微不足道的:
// gotta chop it faster saw = new ChainSaw(); sawMill.setSaw(saw); sawMill.run();
Ran Biron.. 15
我有完全相同的问题,并且答案是这样的:
当然,你可以做你所描述的"然后你可以简单地做:......"(让我们称之为"A级").但是,这会将A类与HandSaw或SawMill类所需的所有依赖关系联系起来.为什么A应该与HandSaw耦合 - 或者,如果你采取更现实的方案,为什么我的业务逻辑应该耦合到DAO层所需的JDBC连接实现?
我之后提出的解决方案是"然后将依赖项更进一步" - 好吧,所以现在我已将我的视图耦合到JDBC连接,我应该只处理HTML(或Swing,选择你的风格).
由XML(或JavaConfig)配置的DI框架通过让您"获得所需的服务"来解决这个问题.你不关心它是如何初始化的,它需要什么工作 - 你只需获得服务对象并激活它.
此外,你对"加号:"(你所做的SawMill springSawMill = (SawMill)context.getBean("sawMill"); springSawMill.run();
)有一个误解- 你不需要从上下文中获取sawMill bean - 应该将sawMill bean注入到你的对象(A类)中DI框架.所以不是...... getBean(...),而是去"sawMill.run()",而不是关心它来自哪里,谁初始化它以及如何.对于你所关心的一切,它可以直接进入/ dev/null,或测试输出,或真正的CnC引擎......关键是 - 你不在乎.所有你关心的都是你的小A级应该按照合同要求做的 - 激活锯木厂.
我有完全相同的问题,并且答案是这样的:
当然,你可以做你所描述的"然后你可以简单地做:......"(让我们称之为"A级").但是,这会将A类与HandSaw或SawMill类所需的所有依赖关系联系起来.为什么A应该与HandSaw耦合 - 或者,如果你采取更现实的方案,为什么我的业务逻辑应该耦合到DAO层所需的JDBC连接实现?
我之后提出的解决方案是"然后将依赖项更进一步" - 好吧,所以现在我已将我的视图耦合到JDBC连接,我应该只处理HTML(或Swing,选择你的风格).
由XML(或JavaConfig)配置的DI框架通过让您"获得所需的服务"来解决这个问题.你不关心它是如何初始化的,它需要什么工作 - 你只需获得服务对象并激活它.
此外,你对"加号:"(你所做的SawMill springSawMill = (SawMill)context.getBean("sawMill"); springSawMill.run();
)有一个误解- 你不需要从上下文中获取sawMill bean - 应该将sawMill bean注入到你的对象(A类)中DI框架.所以不是...... getBean(...),而是去"sawMill.run()",而不是关心它来自哪里,谁初始化它以及如何.对于你所关心的一切,它可以直接进入/ dev/null,或测试输出,或真正的CnC引擎......关键是 - 你不在乎.所有你关心的都是你的小A级应该按照合同要求做的 - 激活锯木厂.
依赖注入是隐式参数传递的简并形式,其目的基本相同,以解决所谓的配置问题:
配置问题是在整个程序中传播运行时首选项,允许多个并发配置集在静态保证分离下安全共存.
依赖注入框架弥补了隐含参数的缺乏,Curried函数以及语言中monad的便利设施.
Spring有三个同样重要的功能:
依赖注入
面向方面的编程
框架类库,用于帮助持久化,远程处理,Web mvc等.
我同意,当你将它与一次调用new相比较时,很难看到依赖注入的优势.在这种情况下,后者肯定会看起来更简单,因为它只是一行代码.Spring的配置总是会增加代码行,所以它不是一个成功的论据.
当你可以从类中进行交叉关注并使用方面以声明方式设置它们时,它开始看起来好多了.与单个"新"调用的比较并不是Spring创建的.
也许使用Spring的最佳结果是它推荐的习惯用法使用接口,分层和DRY这样的好原则.这真的只是Rod Johnson在他的咨询演出中使用的面向对象最佳实践的升华.他发现随着时间的推移,他建立的代码帮助他为客户提供更好的软件.他总结了他在"Expert 1:1 J2EE"中的经验,最终将代码公开为Spring.
我想说,在你认为他的经验可以帮助你编写更好的代码的程度上买入框架.
在你将所有这三个功能结合起来之前,我认为你无法获得Spring的全部价值.
当然,这是一个受到尊重的例子,对于更复杂的对象关系,存储XML文件可能比以编程方式编写文件更有效,但肯定必须有更多的东西呢?
我认为将"接线"放在配置文件中而不是在代码中手动执行更有意义,原因如下:
配置在代码外部.
对连接的更改(告诉您sawmill
使用不同的实例Saw
)可以简单地对外部(XML)文件进行更改,不需要更改代码,重新编译,重新部署等.
当你有几十个类和几个注入层时(例如:你有一个web Controller
类,它获取一个Service
包含你的业务逻辑的类,它使用a DAO
来Saw
从数据库中获取s,并将其DataSource
注入其中,等等. ),手动连接协作者是繁琐的,需要几十行代码,除了连接之外什么都不做.
这有点不那么明确的"好处",但是通过在代码外部进行所有"连接",我认为它有助于向开发人员重新强调依赖注入的核心思想,特别是编码到接口,而不是实现.通过手动接线,可以很容易地回到原来的状态.
我通常不关心XML或基于DI的DI,因为在我的用例中,它增加了不必要的复杂性.相反,我通常会选择某种形式的手动DI,对我来说,这似乎更自然,并且具有大部分好处.
public class SawDI{ public Saw CreateSaw(){ return new HandSaw(); } public SawMill CreateSawMill(){ SawMill mill = new SawMill(); mill.SetSaw(CreateSaw()); return mill; } } // later on SawDI di = new SawDI(); SawMill mill = di.CreateSawMill();
这意味着我仍然集中了耦合并具有其所有优点,而不依赖于更复杂的DI框架或XML配置文件.