Java和C#中IOC技术比较分析

       一直想用心写这个系列的文章,其实看得越多,也就越觉得自己在这方面的功力太浅,也就越不想班门弄斧啦,作为一个开篇,我想把这个技术深层次化,在之前的.net 的一个 MVC 系列文章其实已经涉及到了,只是 .net 在这方面的应用不如java 来得这么猛烈,这么酣汗淋漓,所以也就下定决心,这一阶段针对这一个技术点进行深层次的剖析。

  IOC ,英文全称Inversion of Control,中文直译控制反转,一般我们称之为“依赖注入”原则,在我还未过多涉足java领域时,在C#语言24个设计模式中(参见《大话设计模式》)有一个依赖倒转原则,作为一个设计模式的原则而不是一个模式。在接触java之后,尤其是spring框架后,被称之为“依赖注入”IOC设计模式。其实大体的意思和意义都是差不多的,只是在我看来,在C#领域对这个技术的应用没有那么广泛,限于.NET技术的先天性,我很少看到这方面的巨作,也许是我太肤浅。在我上次写基于.NET的MVC ProDinner系列文章时候,查找这方面的文章,几乎是为零,只能自己去看MVC源码或者Castle 项目源码。

  首先看看《大话设计模式》中基于C#语言如何诠释这门艺术的。原著由维修计算机入题,在我们日常生活中,我们很自然地,提高计算机速度需要更高的CPU,需要更快的系统响应度就需要更好的内存,需要更大的存储介质,就需要扩大硬盘,在这一系列的活动中,于是我们就自然而然的发现,计算机速度依赖CPU,在CPU的使用中我们从来不关心CPU是怎么设计出来的,我们只需要关注这是Intel还是AMD的CPU,因为他决定了你的主板类型,当确定了CPU之后,我们只需关注你需要使用那款CPU,因为接口主板都是一样的,也即是说,大的功能不会去关注具体每一款产品的实现,而只需要关注产品与产品之间的标准化接口。由此引出来这个原则的定义:

  A、高层模块不应该依赖低层模块,两个都应该依赖抽象
  B、抽象不应该依赖细节,细节应该依赖抽象

  这么讲其实是比较抽象的,具体到代码中实际上就是,层于层之间,各模块间,各项目间都应该使用接口层来完成模块的融合。 当某个角色(可能是一个对象实例,调用者)需要另一个角色(另一个对象实例,被调用者)的协助时,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。而控制反转中创建被调用者的工作不再由调用者来完成。创建被调用者 实例的工作通常由特定的容器来完成,然后注入调用者,因此也称为依赖注入。
  废话不多说,理论讲得再精彩最终看代码实现,依据我对这些该概念的理解,我们一起看基于C#语言写出的实例:

   需求场景:
      父亲在完成一件事情的时候,由于各方面原因需要自己的子女帮忙才能完成,所以,父亲需要去调动自己的儿子或者女儿才能完成这件事情
       类型设计:
  接口 Iperson为被调用者的基类型
      类 Father 调用者
      类 Son 被调用者
  类 Daughter 被调用者
  类 Container 依赖注入容器

#FormatImgID_0#

      程序分析:

      Father类通过调用抽象层接口,来完成这个操作,至于调用儿子还是女儿我们在构造函数中完成类型的选择。也就是说Father是调用者,Son和Daughter是被调用者,Container是依赖注入的生成容器。负责将调用者Son或者Daughter对象实例化给调用者。
  .NET 程序目录结构:

#FormatImgID_1#


  打开visual studio 建立控制台应用程序,接口层代码实现:

  1. //接口层
  2.     public interface Iperson
  3.     {
  4.          void operation();
  5.     }
复制代码


调用者代码实现:
  1. //调用者实现类
  2.     public class Father
  3.     {
  4.         Iperson iperson;

  5.         Container container = new Container();

  6.         public Father(String typeName)
  7.         {
  8.             iperson = container.GetApplication(typeName);
  9.         }
  10.         public void operation()
  11.         {
  12.             iperson.operation();
  13.         }
  14.     }
复制代码



Container 在这里我直接new出实例了,看到这个代码熟悉java spring框架的同学就似曾相识了,一般是用@autowired来修饰依赖注入的操作句柄。Father类中的operation方法并没有自己去实现任何操作,而是调用了iperson的操作来实现自己的方法,而iperson只是一个接口类型,需要具体的实现类,代码才能运行起来,我们看iperson的两个实现层:


  1. //被调用者类 ipseron实现层
  2.     public class Son:Iperson
  3.     {
  4.         public void operation()
  5.         {
  6.             Console.WriteLine("son is the operator");        
  7.         }
  8.     }

  9.     //被调用者之二
  10.     public class Daughter:Iperson
  11.     {
  12.         public void operation()
  13.         {
  14.             Console.WriteLine("daughter is the operator");
  15.         }
  16.     }
复制代码



Conatiner是实现容器,原理其实很简单,反射技术,反射原理进行不同的封装其实就形成了不同的技术框架,java spring的ioc核心离不开反射,许多的数据库mapper框架同样也离不开反射,看看代码实现:
  1. //生成实例容器
  2.     public class Container
  3.     { 
  4.         
  5.         public Iperson GetApplication(String typeName)
  6.         {
  7.           return (Iperson) Activator.CreateInstance(Type.GetType(typeName));
  8.         }
  9.     }
复制代码




当然,我这是最基本的反射实现了,在生成化产品化的框架中反射远没有这么简单,但原理都是相同的。好,看下测试类实现代码:

  1. class Program
  2.     {
  3.         static void Main(string[] args)
  4.         {
  5.             //调用son
  6.             Father fa = new Father("IOCblog.Son");
  7.             fa.operation();

  8.             //调用daughter
  9.             fa = new Father("IOCblog.Daughter");
  10.             fa.operation();
  11.         }
  12.     }
复制代码



程序运行结果:
#FormatImgID_2#

代码写到这里对这个概念有所掌握的同学,其实会是有共鸣的,这是依赖注入最基本的实现了,日常项目开发中基于框架级别来实现这种模式思想,我们很多时候是用修饰符或者配置文件,典型的就是java下面的spring的实现。java spring框架可没那么简单,spring两大核心ioc和aop,实现的手段无非也是如此,但不同的是思想!思想!我这里如若再用java套spring框架来做示例实现java下的IOC的思想,肯定也就没人有兴趣继续看下去了,这篇文章的含金量就没那么诱人啦,那么我们来高级点的:


基于java代码的模拟spring框架IOC实现

  实现java下的ioc毫无疑问,我们首先专业术语就换成了,我们需要JavaBean来实现注入,需要factory来实现容器,实现步奏:
  1、建立需要实现注入的JavaBean,注入类Person和容器类Persons
  2、建立类似spring框架的application.xml配置文件,将依赖的JavaBean和相应注入的属性配置在xml文件中,我们这里取名叫 IocApplication.xml
  3、实现注入容器的factory类,主要负责读配置文件->依据配置文件进行对象实例化->放入persons map集合当中,以备调用。
java程序目录结构:

#FormatImgID_3#

大体实现思路如上所述,好,废话不多说,看看代码如何实现,第一步和第二步代码如下( 切换到eclipse下,建立java应用程序 ):

  1. package com.ioc.bean;

  2. //被调用注入类
  3. public class Person {
  4.     
  5.     private String name;
  6.     public String getName()
  7.     {
  8.         return this.name;
  9.     }
  10.     
  11.     public void setName(String name)
  12.     {
  13.         this.name = name;
  14.     }

  15. }
  16. package com.ioc.bean;

  17. //beans类
  18. public class Persons {

  19.     private Person son;
  20.     public Person getSon()
  21.     {
  22.         return this.son;
  23.     }
  24.     
  25.     public void setSon(Person son)
  26.     {
  27.         this.son = son;
  28.     }
  29.     
  30.     private Person daughter;
  31.     public Person getDaughter()
  32.     {
  33.         return this.daughter;
  34.     }
  35.     
  36.     public void setDaughter(Person daughter)
  37.     {
  38.         this.daughter = daughter;
  39.     }
  40. }
复制代码


实现的容器factory代码量比较大,这里就不依依贴出,所有的源代码都在附件中可自由下载,这里主要分析几个关键步奏:

实现容器,主要负责一件事件,通过配置的xml,对相应的javabean进行反射,生成实例化对象,存入map中,xml配置实现:
  1. <?xml version="1.0" encoding="UTF-8"?> 
  2. <beans> 
  3. <bean id="son" class="com.ioc.bean.Person"> 
  4. <property name="name"> 
  5. <value>mark</value> 
  6. </property> 
  7. </bean> 
  8. <bean id="daughter" class="com.ioc.bean.Person"> 
  9. <property name="name"> 
  10. <value>cindy</value> 
  11. </property> 
  12. </bean> 
  13. <bean id="father" class="com.ioc.bean.Persons"> 
  14. <property name="son"> 
  15. <ref bean="son" /> 
  16. </property> 
  17. <property name="daughter"> 
  18. <ref bean="daughter" /> 
  19. </property> 
  20. </bean> 
  21. </beans>
复制代码


容器实现map操作代码:
  1. private Map<String,Object> beanMap=new HashMap<String,Object>();  
  2.       
  3.     /** 
  4.      * 初始化xml文件 
  5.      * */  
  6.     public void init(String xmlUrl){  
  7.         SAXReader  saxReader=new SAXReader();  
  8.         File file=new File(xmlUrl);  
  9.         try{  
  10.             saxReader.addHandler("/beans/bean",new BeanHandler());  
  11.             saxReader.read(file);  
  12.         }  
  13.         catch(DocumentException e){  
  14.             System.out.println(e.getMessage());  
  15.         }  
  16.     }  
  17.       
  18.     /** 
  19.      * 根据beanid来获取bean 
  20.      * */  
  21.     public Object getBean(String beanId){  
  22.         Object obj=null;  
  23.         obj=beanMap.get(beanId);  
  24.         return obj;  
  25.           
  26.     }
复制代码


BeanHandler毫无疑问就是具体去操作IocApplication.xml文件的操作类,代码比较长,可以下载附件源代码查看。
好,看下测试类代码
  1. package com.ioc.test;

  2. import com.ioc.bean.Person;
  3. import com.ioc.factory.BeanFactory;

  4. public class Test {

  5.     public static void main(String[] args) {
  6.         // TODO Auto-generated method stub

  7.         String xmlUrl="src/IocApplication.xml";  
  8.         BeanFactory factory=new BeanFactory();  
  9.         factory.init(xmlUrl);  
  10.         Person me=(Person)factory.getBean("son");  
  11.         System.out.println("Son's name:"+me.getName()); 
  12.     }

  13. }
复制代码


程序运行结果:
#FormatImgID_4#

 结束语:
  这篇文章我们暂且不来比较依赖注入和反转的区别,依赖反转和依赖注入很多程序员把这两个概念当成一个理解,认为只是不同的解释,其实细分还是有区别的

  1、依赖注入是从应用程序的角度在描述,也就是说,应用程序依赖容器创建并注入它所需要的外部资源;而控制反转是从容器的角度在描述,容器控制应用程序,由容器反向的向应用程序注入应用程序所需要的外部资源。

  2、IOC,控制反转是软件运行时体现出来的一个特征:如果对象A运行时依赖于对象B,但A并不去创建B,而是从外界直接取得B。也就是说,一个对象并不是自己去创建它所依赖的其它对象。DI,依赖注入是控制反转的一种实现手段。如上面的例子,B的取得并不需要A的干涉,而是利用某些框架在通过构造参数或属性设置来实现。
关于这些这里就不展开叙述,后面我们再详细讨论,这节主要讨论依赖注入,我们首先在这节暂时认为这是一个相同的概念。作为概述,首先我们了解下IOC概念,再看看java和C#在这一概念上的运用比较。(文章来源:http://www.mfqyw.com/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值