Spiga

理解依赖注入及其好处!

2010-02-09 09:04 by w i n s o n, 2993 visits, 收藏, 编辑

关于依赖注入我看园子里已早有人发过相关的文章了,只是主要都是从理论角度出发,是一些虚拟的例子,感觉要真正运用起来的话可能有些朋友还不太清楚,最近我在公司的项目中就遇到一个比较麻烦的问题,正好可以使用依赖注入来解决,所以顺便写下我的心得,也方便自己以后查阅

 

最近在公司的项目中,遇到的问题是:我们需要使用一个第三方写的一个的控件(open source的)来生成组织架构的Chart图,其中在chart图里需要显示员工的头像,在控件里只支持输入图片流以直接生成员工头像,而在我们项目里,只能直接引用了 webserver 后才可以调用获取头像图片的流数据,所以如果要使用此chart控件的话,就还需要在chart的项目里去引用我们自己的 webserver 以调用里面的方法,但这显然是不可行的,这个chart控件是一个完全独立的第三方控件,这样引用 webserver 的话耦合性太强了,万一以后 webserver 修改来,就必须重新编译那个控件的DLL,所以只好另想办法了


这时我就想到了一个非常适合的设计模式,就是依赖注入,它能消除组件间的直接依赖关系,让组件的开发更独立,使用更灵活。依赖注入主要有以下几种:包括接口注入、构造函数注入和属性注入。

以下我就是使用接口注入来解决上述问题的:
1、先创建一个接口,实现具体需要调用的功能:
 public interface IGetImageProvider
 {
        Stream ReadFileToMemoryStream(
string empNo); //一个获取图片的方法,此方法需调用 webserver 里相关函数完成的,这里利用接口先创建方法的定义
 }


2、创建一个注入用的接口,实现2个控件间的依赖:
public interface IGetImageMgr
{
        
void InjectImage(IGetImageProvider getImage);//依赖注入的方法,让2个控件间的联系起来了
}


3、为独立的 chart 控件项目继承 IGetImageMgr 接口,然后实现 InjectImage 方法:
代码
private IGetImageProvider getImage;//定义 IGetImageProvider 接口,以实现 webserver 的方法

public void InjectImage(IGetImageProvider getImage)
{
    
this.getImage = getImage; //实现 IGetImageMgr 接口的注入方法,这步是最主要的,有了这个从而就实现 webserver 里的方法了
}


4、在  chart项目里调用上而的接口方法,以获取图片信息,然后进行处理:
Stream imageStream = getImage.ReadFileToMemoryStream("0001");//直接调用接口方法,此方法将会实现 webserver 里的相关函数的功能,这样就不需要引用 webserver 也能独立完成了。


OK,完成了独立控件里的代码,现在在项目里引用此控件的DLL,然后创建一个类以实现 IGetImageProvider 接口,为2个项目间创建联系:
1、创建一个 GetImage 类,继承 IGetImageProvider 接口,这时就可真正实现里面的获取图片的方法了:
public class GetImage : IGetImageProvider
{
     
public Stream ReadFileToMemoryStream(string empNo)
     { 
//do something.... }
}

2、实例化 IGetImageProvider 接口和 chart 控件,完成依赖注入的调用:
代码
IGetImageProvider getimage = new GetImage();//创建接口对象

OrgChart myOrgChart 
= new OrgChart();//创建 chart 控件对象

myOrgChart.InjectImage(getimage); 
//以接口注入方式为 chart 控件的对象赋值以达到依赖注入的效果,这时就可直接在 chart 控件内部直接调用 IGetImageProvider 接口的实现方法了

 

以过以上的调用后,就可以让2个项目之间解耦合,让开发出来的控件更加独立,而且方便扩展

标签: 依赖注入
Add your comment

24 条回复

  1. #1楼 debugger      2010-02-09 09:25
    petshop4.0就是个活例子 还用在园子里看?
     回复 引用 查看   
  2. #2楼[楼主] w i n s o n      2010-02-09 09:33
    引用debugger:petshop4.0就是个活例子 还用在园子里看?

    嗯,是的,但并不是每个人都会去慢慢研究petshop的,感觉这个项目比较大,如果只想了解下依赖注入,不需要去看这么大堆代码,在园子里看的技术都是一点一滴的,大家各自根据需要查阅就是了,我想你也不会为了其中一个小技术去看一大本很厚的书吧,呵呵。
     回复 引用 查看   
  3. #3楼 風語者·疾風      2010-02-09 09:38
    嗯,写的很实在。IOC其实最大用处就是依赖倒置,否则还不如工厂好用。
     回复 引用 查看   
  4. #4楼 不若相忘于江湖      2010-02-09 09:48
    我想问一下, 如果我直接在项目中新建一个类,

    这个类里直接引用WEB服务, 而第三方控件引用这个类的方法

    这样做跟依赖注入有区别吗。 我觉得好像都差不多。

     回复 引用 查看   
  5. #5楼[楼主] w i n s o n      2010-02-09 09:53
    引用不若相忘于江湖:
    我想问一下, 如果我直接在项目中新建一个类,

    这个类里直接引用WEB服务, 而第三方控件引用这个类的方法

    这样做跟依赖注入有区别吗。 我觉得好像都差不多。


    你这样做后,控件与项目间的不就耦合度很大了?因为你控件要依赖与项目里的类来实现,如果此控件放到其他项目里,那就不能用了?除非要修改控件的源码去引用新的类?

    使用依赖注入后,控件放到其他项目里都可以用,只需在新项目里去实现控件里的注入接口即可,也符合OO的原则哦
     回复 引用 查看   
  6. #6楼 ξ箫音ξ      2010-02-09 09:56
    应该有大局观的思想去进行编程工作;IOC实际上是解决OO思想中不同对象和对象之间耦合性高的问题;设计模式只是一种复合重复应用的结构化模型。
     回复 引用 查看   
  7. #7楼 木神易      2010-02-09 09:56
    楼主个人blog不错啊,相册也挺漂亮的!
     回复 引用 查看   
  8. #8楼 Miko Ling      2010-02-09 09:58
    控件既然是开源的,那我可以修改代码增加调用接口嘛,岂不更简单?
     回复 引用 查看   
  9. #9楼[楼主] w i n s o n      2010-02-09 09:58
    @木神易
    呵呵,谢谢:)
     回复 引用 查看   
  10. #10楼 流箫      2010-02-09 10:10
    不错,,, 顶下。。
     回复 引用 查看   
  11. #11楼[楼主] w i n s o n      2010-02-09 10:50
    引用Miko Ling:控件既然是开源的,那我可以修改代码增加调用接口嘛,岂不更简单?

    现在我就是修改了控件内部代码才得以完成的,不知你说的修改是指直接在控件添加一个函数让外部调用还是指添加接口来另外去实现?由于本例中需先根据员工号然后得到图片流(图片放在服务器,并已有完成的获取方法了),所以我感觉使用Ioc是比较方便而且对现有代码改动最少的方法了,如果有更好的方法,也欢迎提出哦
     回复 引用 查看   
  12. #12楼 深蓝医生      2010-02-09 11:00
    请问你引用WebService的项目是那个项目呢?还是没有明白你的所有项目脱离了对WebService的依赖。
     回复 引用 查看   
  13. #13楼 鬼话符      2010-02-09 11:01
    路过。。。
    如果想详细了解DI的话
    大家可以看看这本书
    Dhanji R. Prasanna
    Design patterns using Spring and Guice

    是一个很牛的人写的 网上能下到原版.
    对于我们理解DI很有帮助。

    学习中。。。。。。。。。。。
     回复 引用 查看   
  14. #14楼[楼主] w i n s o n      2010-02-09 11:06
    引用深蓝医生:请问你引用WebService的项目是那个项目呢?还是没有明白你的所有项目脱离了对WebService的依赖。


    其实本例中有3个项目,一个是UI的,一个是webservice,另一个是独立的控件项目,而webservice的项目是获取员工图片信息的,然后在UI里通过独立控件项目来生成相关的chart图
     回复 引用 查看   
  15. #15楼 Todd Wei      2010-02-09 11:21
    引用風語者·疾風:嗯,写的很实在。IOC其实最大用处就是依赖倒置,否则还不如工厂好用。

    inversion of control与dependency inversion虽然有联系,但本质上讲的不是一回事。
     回复 引用 查看   
  16. #16楼 mj      2010-02-09 11:23
    引用w i n s o n:
    引用Miko Ling:控件既然是开源的,那我可以修改代码增加调用接口嘛,岂不更简单?

    现在我就是修改了控件内部代码才得以完成的,不知你说的修改是指直接在控件添加一个函数让外部调用还是指添加接口来另外去实现?由于本例中需先根据员工号然后得到图片流(图片放在服务器,并已有完成的获取方法了),所以我感觉使用Ioc是比较方便而且对现有代码改动最少的方法了,如果有更好的方法,也欢迎提出哦


    控件既然使用流作为输入,那提供一个函数让外部调用应该是必备的功能,外部只需要调用这个函数,把流作为参数传入不就行了?这样改是不是简单直接,通用性更好呢?
     回复 引用 查看   
  17. #17楼 深蓝医生      2010-02-09 11:49
    引用w i n s o n:
    引用深蓝医生:请问你引用WebService的项目是那个项目呢?还是没有明白你的所有项目脱离了对WebService的依赖。


    其实本例中有3个项目,一个是UI的,一个是webservice,另一个是独立的控件项目,而webservice的项目是获取员工图片信息的,然后在UI里通过独立控件项目来生成相关的chart图


    也就是在UI项目里面引用了WebService项目,是吗?还是说这三个项目之间没有直接的依赖关系?(或者说各个项目没有直接引用其他项目或者其编译好的DLL)
     回复 引用 查看   
  18. #18楼[楼主] w i n s o n      2010-02-09 11:51
    引用深蓝医生:
    引用w i n s o n:
    引用深蓝医生:请问你引用WebService的项目是那个项目呢?还是没有明白你的所有项目脱离了对WebService的依赖。


    其实本例中有3个项目,一个是UI的,一个是webservice,另一个是独立的控件项目,而webservice的项目是获取员工图片信息的,然后在UI里通过独立控件项目来生成相关的chart图


    也就是在UI项目里面引用了WebService项目,是吗?还是说这三个项目之间没有直接的依赖关系?(或者说各个项目没有直接引用其他项目或者其编译好的DLL)


    是的,现在是直接在UI里引用了webservice
     回复 引用 查看   
  19. #19楼 陛下      2010-02-09 14:58
    ioc确实应该好好学学,我现在还是自己手动写个工厂类并用反射调用创建目标接口的实例。时间啊,岁月啊,那么的不等人,呵呵。
     回复 引用 查看   
  20. #20楼 dragonpig      2010-02-10 08:14
    如果这个第三方chart控件只是依赖一个stream,那就在生成图片的方法传个stream进去。随便说说,不很清楚具体场景是怎样的。
     回复 引用 查看   
  21. #21楼[楼主] w i n s o n      2010-02-10 08:21
    引用dragonpig:如果这个第三方chart控件只是依赖一个stream,那就在生成图片的方法传个stream进去。随便说说,不很清楚具体场景是怎样的。


    呵,这个方法不行,如果可以,我也不用大费周折了,因为在控件里生成图片并不是逐个记录进行的,而是先传一个Datatable进去,然后再利用DT里的信息(包括员工号和姓名等)来一次过在控件里完成所有工作,而里面最终是以XML形式保存这些结构,因为要做到节点层层展开的效果(这就是控件内部的架构机制,也不需修改),所以也不能直接传stream进去处理,只能先得到字符串的员工号,再从控件里去获取这些图片的stream了
     回复 引用 查看   
  22. #22楼 dytes      2010-02-10 16:15
    不错!
    通过Interface Injection的方式实现DI一个生动的示例。
    平时网上看到的大部分是Constructor Injection 和Setter Injection的方式。
     回复 引用 查看   
  23. #23楼 疯流成性      2010-03-05 11:28
    如果想独立的话我觉得不应该使用ioc,这样做就是强迫别人也使用ioc。
     回复 引用 查看   
  24. #24楼 zhdw      2010-04-06 15:39
    问下:
    1.如果Chart不是开源的,咋办?LZ所谓的IOC还是改动了源码:“为独立的 chart 控件项目继承 IGetImageMgr 接口,然后实现 InjectImage 方法”,可见所谓的IOC太勉强了。
    2.个人以为用facade模式好像完全可以解决LZ的问题
    3.感觉“不相忘于江湖”的看法有道理,而LZ的回答似是扯谈,因为增加了接口,强迫了引用和调用,谁喜欢“被XX”?
     回复 引用 查看