因为最近在看Pico的一些资料,所以对IoC(或者叫Dependency Injection)相关的其他东东也关注了一下。
如果对IoC不熟悉,你可以在今年第三期的《程序员》上面,找到一篇Martin Fowler写的介绍文章。英文原文在这里。
在Java平台上,基于IoC的轻量级Container已经不少了,像大家熟悉的Spring、PicoContainer(NanoContainer)、Apache Avalon(此Avalon可不是Longhorn里面的那个Avalon)等等,但是.NET平台下除了一个Pico的移植和Spring作者开发Spring.Net的计划外,就少有所见了。
当然有人很不服气,这篇文章的作者认为,IoC根本不是什么新概念,在2000年.NET的测试版里面,就可以看到IoC的影子了。在System.ComponentModel下面,就隐藏着IComponent、IContainer、ISite、IServiceProvider、IServiceContainer等接口和相应的默认实现。
模仿Martin Fowler那篇文章中的代码,我用这些接口和默认实现写了一个IoC的“最小实现”,除了创建了一个新的MyServiceContainer类用来对ServiceContainer做了一下包装之外,其他全部是利用的.Net Framework本身提供的类和接口。
// 创建一个Container
IServiceContainer container = new MyServiceContainer();
// 在Container中注册新的组件
container.AddService(typeof(IMovieFinder), new DefaultMovieFinder());
container.AddService(typeof(IMovieLister), new DefaultMovieLister());
// 也可以从Container中删除组件
//container.RemoveService(typeof(IMovieFinder));
// 从Container中获取指定接口的组件后调用其功能
IMovieLister lister = (IMovieLister) container.GetService(typeof(IMovieLister));
if (lister != null)
{
lister.ListMovieByName("kaneboy");
}
注意上面代码中,在注册新的组件时,仍然用的HardCode创建新的实例,这里仅作为演示(通常实例类型是放在配置文件中或者完全按照配置文件来自动在Container中创建Component)。
再来看看DefaultMovieFinder的实现:
public class DefaultMovieFinder : System.ComponentModel.Component, IMovieFinder
{
public String[] FindByName(String name)
{
return new String[1] {"Matrix"};
}
}
DefaultMovieFinder是一个实现了IMovieFinder接口并从System.ComponentModel.Component继承下来的类。
public class DefaultMovieLister : System.ComponentModel.Component, IMovieLister
{
public void ListMovieByName(String name)
{
IMovieFinder finder = (IMovieFinder) this.Site.GetService(typeof(IMovieFinder));
if (finder != null)
{
foreach(String movie in finder.FindByName(name))
{
Console.WriteLine(movie);
}
}
}
}
上面是DefaultMovieLister的实现,它是一个实现了IMovieLister并从System.ComponentModel.Component继承下来的类。在ListMovieByName()方法中,它通过Component类自带的Site属性(一个实现了ISite接口的类)来获得了Container中所需的Component。
如果对完整源码感兴趣,可以在这里下载完整的源码项目文件。