之前对于为什么要用spring感到迷惑,看了《spring in action 中文版》后终于明白。这本书极力推荐。
1.IOS(控制反转)
书中用了一个很好的例子,圆桌骑士和探险任务。大概描述一下:
classBigSoldiers {
String name;
HorseQuest quest;//寻找好马的任务
BigSoldiers (String name){
this.name = name; //构造士兵
quest = new HorseQuest (); //获得探险任务
}
quest.runQuest(); //执行任务
}
class HorseQuest {
HorseQuest (){..}
public void runHorseQuest throws HorseNotFoundException() {
...、//执行任务
}
}
可以发现在BigSoldiers 中因为要用到HorseQuest类,所以BigSoldiers 中充斥了很多的HorseQuest类的代码。
当我们测试BigSoldiers 类的时候也就间接测试了HorseQuest类,
这样做的后果是带来两个类的紧密耦合。并且我们不能测试所有的情况,(如HorseQuest
返回null或抛出HorseNotFoundException的情况)。当HorseQuest有修改的时候,可能也
要修改BigSoldiers 类。
这对oop来说不是好的状态。我们应该给与BigSoldiers 更好的独立性。
面对紧耦合,我们应想办法解耦合。把类隐藏在接口下是解耦合的一种好方法。
对代码作如下修改:
士兵是一个人,先定义士兵的接口
public interface People{
public Object runQuest()
}
我们让BigSoldiers 类继承People
class BigSoldiers implements People{
String name;
Quest quest;//寻找好马的任务
BigSoldiers (String name){
this.name = name; //构造士兵
quest = new HorseQuest (); //获得探险任务
}
public Object runQuest(){
return quest.runQuest(); //执行任务
}
}
到用的时候,我们就直接调用接口,实现类隐藏在接口下,到时修改类的时候
就不会有造成牵一发而动全身。按作者所说的,这样,我们朝正确方向又进了一步。
同样,我们修改任务类
定义任务
public interface Quest {
public ObjectquestOver();
}
class HorseQuest implements Quest{
HorseQuest (){..}
public ObjectquestOver throws HorseNotFoundException() { //任务结束
...、//找马
}
}
这样,我们调用接口就能赋予士兵任务,但不足的是士兵现在只能同样一个任务(在这里是找好马),
也就是一个士兵对应一个任务实例。并且士兵是他自己主动去找任务来执行的。这不符合我们的思维习惯。
士兵应该是被动接受任务,而不是自己决定的。我们应这样再修改
class BigSoldiers implements People{
String name;
Quest quest;//寻找好马的任务
BigSoldiers (String name){
this.name = name; //构造士兵
quest = new HorseQuest (); //获得探险任务
}
public Object runQuest(){
return quest.runQuest(); //执行任务
}
pblic void setQuest(Quest quest){
this.quest = quest;
}
}
注意加粗字体的改变,这样我们将采用方法注入依赖对象。
也就是士兵将是被动地接受任务了。当然,我们还需要在
applicationContext.xml中具体装配起来。
sping的ioc就是基于这样的思想,通过依赖注入的方法,把类与依赖类的关系统一在xml文件
中管理,易于管理修改。而且使各个类的耦合松散,功能清晰。