三、基于配置文件和Reflection的工厂模式为了消除MainApp对其它组件的依赖性,我们引入工厂模式,并且根据配置文件指定的装配规程,利用.net提供的反射技术完成对象的组装工作。本部分代码仅仅提供一种功能演示,如果实际应用仍需进一步完善(建议使用一些成型的Ioc框架,例如Spring.net或Castle等)。经过改造后的系统,组件间依赖关系如下图:øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
可以看出这次实现了真正的“针对接口编程”。所有的组件只依赖于接口。MainApp所需的对象是由工厂根据配置文件动态创建并组装起来的。当系统需求发生变化时,只需要修改一下配置文件就可以了。而且MainApp、SayHello和HelloGenerator之间不存在任何的依赖关系,实现了松耦合。øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
这是如何实现的呢?我们首先要能够解析配置文件中的信息,然后建立包含相关信息的对象。最后根据这些信息利用反射机制完成对象的创建。首先我们看一下配置文件所包含的内容:øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
<?xml version="1.0" encoding="utf-8" ?>øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
<configuration>øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
  <configSections>øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
      <sectionGroup name="IocInCSharp">øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
        <section name="objects" type="IocInCSharp.ConfigHandler, MainApp" />øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
      </sectionGroup>øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
  </configSections>øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
  <IocInCSharp>øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
      <objects>øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
        <object name="SayHello" assembly="SayHello.dll" typeName="IocInCSharp.SayHello">øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
            <property name="HelloGenerator" assembly="HelloGenerator.dll"øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
                      typeName="IocInCSharp.CnHelloGenerator"></property>øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
        </object>øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
      </objects>øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
  </IocInCSharp>øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
</configuration>øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
从中我们可以看出,我们实现了一个IocInCSharp.ConfigHandler类,用来处理配置文件中IocInCSharp\objects结点中的内容。ConfigHandler类将根据该结点下的内容处理并创建一ConfigInfo对象(关于ConfigInfo、ObjectInfo以及PropertyInfo的代码可自行查看源代码,这里就不再赘述)。ConfigHandler类的代码实现如下:øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
using System;using System.Configuration;using System.Xml;namespace IocInCSharp{øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
  public class ConfigHandler:IConfigurationSectionHandlerøÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
  {øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
      public object Create(object parent, object configContext, System.Xml.XmlNode section)øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
      {øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
        ObjectInfo info;øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
        PropertyInfo propInfo;øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
        ConfigInfo cfgInfo = new ConfigInfo();øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
        foreach(XmlNode node in section.ChildNodes)øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
        {øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
            info = new ObjectInfo();øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
            info.name = node.Attributes["name"].Value;øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
            info.assemblyName = node.Attributes["assembly"].Value;øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
            info.typeName = node.Attributes["typeName"].Value;øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
            foreach(XmlNode prop in node)øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
            {øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
              propInfo = new PropertyInfo();øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
              propInfo.propertyName = prop.Attributes["name"].Value;øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
              propInfo.assemblyName = prop.Attributes["assembly"].Value;øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
              propInfo.typeName = prop.Attributes["typeName"].Value;øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
              info.properties.Add(propInfo);øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
            }øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
            cfgInfo.Objects.Add(info);øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
        }øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
        return cfgInfo;øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
      }øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
  }øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
}øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
通过ConfigHandler的解析,我们最终得到一个ConfigInfo实例,Factory就是根据这个实例中所包含的配置信息,利用反射技术对所需对象生成并组装的。SayHelloFactory的代码如下:øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
using System;using System.IO;using System.Configuration;øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
using System.Reflection;namespace IocInCSharp{øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
  public class SayHelloFactoryøÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
  {øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
      public static object Create(string name)øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
      {øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
        Assembly assembly;øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
        object o = null;øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
        object p;øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
        string rootPath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
                          Path.DirectorySeparatorChar;øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
        ConfigInfo cfgInfo = (ConfigInfo)ConfigurationSettings.GetConfig("IocInCSharp/objects"); øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
        ObjectInfo info = cfgInfo.FindByName(name);øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
        if(info != null)øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
        {øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
            assembly = Assembly.LoadFile(rootPath + info.assemblyName);øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
            o = assembly.CreateInstance(info.typeName);øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
            Type t = o.GetType();øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
            for(int i=0; i<info.properties.Count; i++)øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
            {øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
                              PropertyInfo prop = (PropertyInfo)info.properties;øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
                             
assembly = Assembly.LoadFile(rootPath + prop.assemblyName);øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
                              p = assembly.CreateInstance(prop.typeName);øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
              t.InvokeMember(prop.propertyName,øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
                  BindingFlags.DeclaredOnly |øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
                  BindingFlags.Public | BindingFlags.NonPublic |øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
                  BindingFlags.Instance | BindingFlags.SetProperty, null, o, new Object[] {p});øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
            }øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
        }øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
        return o;øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
      }øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
  }øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
}øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
在上面这段代码中,重点注意三条命令的使用方法:øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
assembly = Assembly.LoadFile(rootPath + prop.assemblyName);p = assembly.CreateInstance(prop.typeName);t.InvokeMember(prop.propertyName,    BindingFlags.DeclaredOnly |    BindingFlags.Public | BindingFlags.NonPublic |    BindingFlags.Instance | BindingFlags.SetProperty, null, o, new Object[] {p});Assembly.LoadFile()用于将外部文件装载进来;assembly.CreateInstance()根据装载进来的程序集创建一指定类型的对象;t.InvokeMember(prop.propertyName, ........BindingFlags.SetProperty, null, o, new Object[] {p})利用反射机制对创建出来的对象设置属性值。 øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
我们的Factory就是利用这种方式根据配置文件动态加载程序集,动态创建对象并设置属性的。有了这个Factory,MainApp中的内容就很简单了:øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
using System;namespace IocInCSharp{øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
  public class MainAppøÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
  {øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
      public static void Main()øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
      {øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
        ISayHello sayHello = (ISayHello)SayHelloFactory.Create("SayHello");øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
        if(sayHello != null)øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
            sayHello.SayHelloTo("zhenyulu");øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
        elseøÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
            Console.WriteLine("Got an Error!");øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
      }øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
  }øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
}øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
现在,MainApp只依赖于接口,不再依赖于其它组件,实现了松耦合。在本例子中,大家可以尝试将配置文件中的IocInCSharp.CnHelloGenerator更改为IocInCSharp.EnHelloGenerator,看看是否输出内容由中文变为了英文。这便是“注入”的效果。 øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø
从上面这个例子我们可以看出,通过自定义配置文件和.net中的Reflection技术,我们自己就可以开发Ioc应用,根据配置文件的信息自行组装相应的对象。但是Reflection编程的技术门槛还是比较高的,并且在实际应用中配置文件的格式、Handler的设计都不是象上面代码那样的简单。不过幸好我们现在有很多的Ioc容器可供选择,它们都提供了完整的依赖注入方式,并且比自己写代码更加成熟、更加稳定。使用这些框架可以让程序员在三两行代码里完成“注入”工作。在我们下一个案例中,我们将使用Spring.net实现依赖注入。我们会发现仅仅添加几行代码并更改一下配置文件就可轻松实现依赖注入。(待续)øÕc:Êg°ðÍwww.netcsharp.cn¥3³á¢íÔø