依赖注入与事件编程

13-03-22 banq
                   

依赖注入或者称反转Ioc,通过第三方框架将你需要依赖的类主动注入进来,依赖注入随着Spring和JavaEE6普及,已经成为大家习惯的一种默认处理类关系的方法。

我将依赖注入和事件编程进行联系比较,是源于某天我突然发现,这两者实际是处理依赖关系的不同方式而已。打个比喻,某个工厂缺少某个部件,通过采购快递将部件送到厂里,这是依赖注射;而有的工厂则相反,委托别的工厂生产好部件后,不拉进自己厂里,而是将自己产品拉出去和那个部件进行组装。

是不是有些绕人,以代码来说明:

public class A{
    B b;

   public void a1(){
      b.xxx();

   }

  public A(B b){
     this.b = b;
  }

}


public class B{

   public void xxx(){
      System.println("hello");
   }


}
<p>

A的方法a1()中需要依赖B的xxx()方法,我们就用Ioc框架将B实例通过A的构造器或者Setter方法注入,注意,其实我们没有发现这里有一个很严重的设计问题,依赖是方法行为的依赖,因为方法有依赖,造成两个整类发生依赖,是不是有点影响面扩大呢?

如果说,通过构造器注入可以保证这两个类的对象生命周期一致,那么通过setter方法进行注入,更容易产生两个类的对象生命周期不一致。而我们很多人适应了Spring的setter方法注入,竟然熟视无睹如此丑陋危险的做法,这也是构造器注入要强于setter方法注入的地方(jdonframework只提供构造器注入的原因),当然,这也只是50步笑百步,从根本上讲,我们不能因为两个类的方法有依赖,就将整个类发生关系,这实际是一种结构偏执思维。

那么如何解决这个问题?从行为模式入手,通过事件模型来解决这个问题。

我们知道事件模型类似观察者模式,有事件的产生者和事件的处理者两个角色,这两个角色通过事件产生了一种联系,实际就是一种依赖。

我们用Guava的事件编程改造上述案例:

public class A{
   EventBus bus;

   public void a1(){
      bus.post("xxx");
   }

  public A(EventBus bus){
     this.bus = bus;
  }
}

public class B{

   @ subscribe
   public void xxx(){
      System.println("hello");
   }
}

EventBus bus = new EventBus();
 bus.register(b);
<p>

事件模型的代码和依赖注入代码的区别是:原来A直接依赖B,现在我们更改为A依赖事件总线对象,注意,这个事件总线其实类似Ioc容器是全局的。换句话说,A对B的直接依赖加入了第三者:

原来:

A --> B

A ----> 第三方 ----> B

这种处理松耦合的方式非常类似我们引入接口,也非常类似facade模式 代理模式等等。

更重要的是,我们避免了因为A的一个方法对B的依赖,而将整个B注入的粗糙做法,泼水泼掉孩子的傻事情不能再做了。

从以上看出,事件编程是对传统依赖注入模式的补充,它们的主要区别是依赖方向的不同:

依赖注入是有点facade模式,大包大揽,以自我为中心,其他人都要来配合我,都注入到我的边界内部,最后我的边界内混乱不堪不说,又会制造新的紧耦合。

事件编程的则从我内部发出事件即可,不要把污七八糟什么人都领到家里,做什么事情完全可以到外面去实施,在家里打个电话指示一下就可以。

本文主要通过实践说明处理依赖关系的两种不同角度,特别是我们进行面向对象编程时,要有很强烈的对象生命周期概念,当我们绑定两个对象时,就如同绑定两个人,这两个人有各自自己的生命轨迹,他们可以在一起共同生活多长时间?如果这些Scope时间边界问题不考虑,那么还不如全部使用静态全局变量编程,避免徒增烦恼。

[该贴被banq于2013-03-22 17:25修改过]

[该贴被admin于2013-03-22 18:39修改过]

                   

20
tangxuehua
2013-03-22 17:41

2013-03-22 17:13 "@banq

"的内容

A ----> 第三方 ----> B ...

这里应该是这样吧:

A ----> 第三方 <---- B

第三方 <---- B:这里你的第三方就是eventbus,那这个箭头表示b订阅eventbus publish出来的事件,本质上就是b依赖于eventbus。而eventbus可以注册任意多个订阅者,所以它其实不依赖于任何一个订阅者。

就像依赖注入的原理一样。

本来a依赖于b,现在改为只依赖一个接口,而b实现了该接口。

a----->接口<-----b

a依赖接口,b实现接口。

[该贴被tangxuehua于2013-03-22 17:46修改过]

[该贴被tangxuehua于2013-03-22 17:47修改过]

masterice
2013-03-23 14:58

好文,前几天就有同事问过我这样的问题。我也觉的不太好,可是最终还是选择了让其注入。原因只有一个,既然用了spring就不想用new。归一还是不归一始终是矛盾的。

yqj2065
2013-03-24 18:31

原本“依赖注入和事件编程”是毫无关系的两个术语,但是一个IoC搞得大家云山雾绕。

例子中A依赖于B,你可以用任意的方式使A依赖于AB而AB依赖于B,与事件模型无关。

例如:

public class A{

AB ab;

public void a1(){ab.xxx();} //AB的xxx()调用B的xxx()即可。

}

依赖注入“框架”spring本身就是和事件编程一样,采用“回调”或Ioc作为基本技术;但是,如果仅仅注入依赖——“A依赖于B”,可以简单地使用一个工具类——使用反射机制+配置文件的静态工厂。

MartinChen
2013-03-25 09:41

最近也做个项目,恰好也涉及到这个,说说我的心得吧

--------以下单为例------------

比如说下单后,需要存数据库,通知仓库备货,检查用户信用。

对于这个三个动作,它们需要由【用户已下单的事件】 来触发。

而完成每一个动作需要的资源,可以用依赖注入的方式来获得。

3Go 1 2 3 下一页