依赖注入
传统的做法,每个对象负责管理与自己相互协作的对象(即它所依赖的对象)的引用,这回导致高度耦合。
我们通过一段代码的过渡来看看依赖注入是如何实现的。
public class DamselRescuingKnight implements Knight {
private RescueDamselQuest quest;
public DamselRescuingKnight() {
this.quest = new RescueDamselQuest();
}
@Override
public void embarkOnQuest() {
quest.embark();
}
}
可以看出,上面的代码大大地限制了骑士的能力,这个骑士只能来拯救女孩。
如果使用了依赖注入,对象的依赖关系将由系统中负责协调各对象的第三方组件在创建对象的时候进行设定,对象无需自行创建或管理它们的依赖关系。
看看升级后的代码:
public class BraveKnight implements Knight {
private Quest quest;
public BraveKnight(Quest quest) {
this.quest = quest;
}
@Override
public void embarkOnQuest() {
quest.embark();
}
public static void main(String[] args) {
BraveKnight knight = new BraveKnight(new RescueDamselQuest());
knight.embarkOnQuest();
}
}
从代码上可以看出明显的好处,骑士变得灵活多了,可以干各种事情,也准备好了做各种事情,你需要好告诉他需要做什么。这是依赖注入的方式之一:构造器注入(constructor injection)
。
上面传入的是一个 Quest,是一个接口,要记住:针对接口而不是针对实现编程
。这样 BraveKnight 没有与任何特定的 Quest 耦合,只要探险任务实现了 Quest 接口,那么具体是那种类型的探险已经无关紧要了,这就是 DJ 所带来的最大收益——松耦合。
应用切面
DI 能够让相互协作的软件组件保持松散耦合,而面向切面编程(aspect-oriented programming, AOP)允许你把遍布应用各处的功能分离出来形成可重用的组件。
系统由许多不同的组件组成,每一个组件负责一块特定功能。除了实现自身核心的功能之外,这些组件还经常承担额外的责任。诸如日志、事务管理和安全这样的系统服务经常融入到自身具有核心业务逻辑的组件中去,这些系统服务通常称为横切关注点,因为它们会跨越系统的多个组件。
你可能想到了要把它们封装成一个独立的模块,然后在需要使用它的组件内部直接调用,比如日志模块,但是方法的调用还是会重复出现在各个模块中。
AOP 能够使这些服务模块化,并以声明的方式将它们应用到它们需要影响的组件中去。所造成的结果就是这些组件会具有更高的内聚性并且会更加关注自身的业务,完全不需要了解涉及系统服务所带来的复杂性。你的核心应用根本不知道它们的存在。