代码改变世界

IoC容器Autofac(2) - 一个简单示例(附demo源码)

2013-03-19 18:20  JustRun  阅读(...)  评论(...编辑  收藏

上篇文章中(IoC容器Autofac(1) -- 什么是IoC以及理解为什么要使用Ioc),我们用自己的方式实现了一个简陋的工厂类来实现IoC.

这里我们尝试使用Auotfac来替换我们的工厂类MovieFinderFactory.

(Autofac的名字应当取的是非常贴切的,它本质上其实就是一个产出对象的自动工厂)

阅读目录:

一. 使用自定义工厂类实现IoC的例子

二. 改造代码,去除MovieFinderFactory

三. 应用Autofac替代工厂类

四. 当需求发生变动, Autofac如何应对?

五. Autofac对程序架构的影响

六. 总结

 

一、使用自定义工厂类实现IoC的例子

我们回顾一下之前的代码:

//这个类的作用是筛选出MPG类型的电影
public class MPGMovieLister:IMovieFinder
{
  public Movie[] GetMPG()
  {
       var finder = MovieFinderFactory.GetFinder();//这里调用工厂类获取具体的实例,得到一个电影列表
       var allMovies = finder.FindAll();
       return allMovies.Where(m => m.Name.EndsWith(".MPG")).ToArray();
   }
}
 

public class MovieFinderFactory
{
     public static IMovieFinder GetFinder()
     {
         return new ListMovieFinder();
     }
} 

public class ListMovieFinder :IMovieFinder
{
   public List<Movie> FindAll()
   {
       return new List<Movie>
                  {
                      new Movie
                          {
                              Name = "Die Hard.wmv"
                          },
                      new Movie
                      {
                          Name = "My Name is John.MPG"
                      }
                 };
   }
}

public interface IMovieFinder { List<Movie> FindAll() }

 

这里MPGMovieLister已经不和具体的MovieFinder耦合了,而是依赖于MovieFinderFactory工厂类提供的IMovieFinder接口的具体实现来取Movie数据。

所以工厂类只要返回不同的实现IMovieFinder的实例,就能够让MovieLister从列表,文本,数据库,web service …… 中获取数据。

二、改造代码,去除MovieFinderFactory

在应用Autofac替换MovieFinderFactory之前,我们先从代码中去掉MovieFinderFactory, 改动之后的代码是这样:

public class MPGMovieLister
{
    private readonly IMovieFinder _movieFinder;
    //增加了构造函数,参数是IMovieFinder对象
    public MPGMovieLister(IMovieFinder movieFinder)
    {
         _movieFinder = movieFinder;
    }

    public Movie[] GetMPG()
    {
     var allMovies = _movieFinder.FindAll();
     return allMovies.Where(m => m.Name.EndsWith(".MPG")).ToArray();
    }
}

public interface IMovieFinder
{
    List<Movie> FindAll()
} 

我们去掉了工厂类MovieFinderFactory, 改造了MPGMovieLister, 添加了一个构造函数, 构造函数要求使用MPGMovieLister时,需要提供一个IMovieFinder的实例。

三、应用Autofac替代工厂类

应用Autofac改造上面的代码。

第一步: 从Nuget中添加Autofac引用

第二步:

* 创建一个ContainerBuilder对象(ContainerBuilder从字面的意思就是用来创建Container(容器)的,而Conainter就是我们从中取各种我们需要对象的地方)

* 注册我们后面将从容器中取出对象的类型。

代码是这样:

var builder = new ContainerBuilder();//

builder.RegisterType<ListMovieFinder>().AsImplementedInterfaces();//注册ListMovieFinder类型,这里的AsImplementedInterfaces表示以接口的形式注册
builder.RegisterType<MPGMovieLister>();//注册MPGMovieLister类型

* 创建容器

_container = builder.Build();

 

第三步: 在程序中使用 _container容器:

var lister = _container.Resolve<MPGMovieLister>();

foreach (var movie in lister.GetMPG())
{
     Console.WriteLine(movie.Name);
} 

 

理解一下Autofac为我们在背后做了什么:

首先,我们注册了类型ListMovieFinder和MPGMovieLister,这样容器就能够知道如何创建这两种类型的实例了。(类其实是创建对象的模板,当我们把模板注册给Autofac, 它就会遵循这个模板为我们提供实例)

后面的代码中,我们调用Resolve方法,取出一个MPGMovieLister的实例。

_container.Resolve<MPGMovieLister>();

这里还有一个需要解释的,对于MPGMovieLister类型,我们为Autofac提供了类型, 但是当Autofac创建MPGMovieLister的实例, 调用它的构造函数的时候,却遇到了问题:

它的构造函数需要提供一个IMovieFinder的实例作为参数的, 聪明的Autofac要在自己的容器里找找,看看没有有办法提供一个IMovieFinder的实例。

这个时候Autofac会发现我们注册过ListMovieFinder, 并且通过AsImplementedInterfaces()方法,指明了就是为接口IMovieFinder提供实例的。

所以Autofac会创建一个ListMovieFinder的实例,作为创建MPGMovieLister时,提供给构造函数的参数。

builder.RegisterType<ListMovieFinder>().AsImplementedInterfaces();

 

四、当需求发生变动, Autofac如何应对?

上面的例子中,我们的类ListMovieFinder实现了IMovieFinder接口, 实际运行中,是由它来提供数据。

假如这个时候,我们要从数据库中获取数据,怎么办?

非常简单,创建一个类DBMovieFinder继承IMovieFinder接口, 然后注册给Autofac就可以了。 这样程序就非常容易的切换到从数据库中取数据了。

注册相关改动的代码是这样的:

var builder = new ContainerBuilder();
builder.RegisterType<ListMovieFinder>().AsImplementedInterfaces();

//这里注册了DBMovieFinder, 这个类继承IMovieFinder接口。因为它也使用了AsImplementedInterfaces,它会覆盖ListMovieFinder的注册。
builder.RegisterType<DBMovieFinder>().AsImplementedInterfaces(); builder.RegisterType<MPGMovieLister>(); 
_container = builder.Build();

 

五、Autofac对程序架构的影响

常见的程序架构大概是: UI层, 业务逻辑层, 持久层(数据层)。

我们可以使用Autofac作为不同层之间的中间人,让UI层依赖于业务逻辑层的抽象接口,业务逻辑层依赖于持久层的接口,而实际运行过程中的实例都由Auotfac来提供。

这样我们就能够解除不同层之间的依赖,将所有的注册类型的操作在一个核心函数或者核心类中实现,那么只要修改这个函数或者类,就能够非常方便的让它们之间的依赖关系发生变化。

 

比如, 在一个大的项目中,持久层和业务逻辑层是并行开发的,而且是不同团队开发,这个时候业务逻辑开发团队的人在没有持久层代码的情况下,如何开始呢?

我们只要定义好持久层的接口, 业务逻辑团队再写一些Stub类(桩类)来实现这些接口,让这些Stub类来替换真正的持久层,所要做的就只是简单的把这些Stub类型注册到Autofac中就可以了。同时做业务逻辑层的单元测试也非常容易了。

 

拿上面的例子来说,就是这个样子:

var builder = new ContainerBuilder();
if(IsLive)//如果是正式环境中,使用DBMovieFinder, 从数据库中获取数据
{
    builder.RegisterType<DBMovieFinder>().AsImplementedInterfaces();
}
else//在开发环境中,先用Stub类ListMovieFinder替代
{
    builder.RegisterType<ListMovieFinder>().AsImplementedInterfaces();
}
builder.RegisterType<MPGMovieLister>(); 
_container = builder.Build();

 

同样的,UI层和业务逻辑层也可以运用同样的思路。

六、 总结

从上面的例子可以看出,使用IoC对于复杂的项目来说,非常有意义,能够为我们搭建一个好的开发层次。

同时,在使用过程中,还能够发现Autofac有以下优点:

1. 可以使用C#代码来完成注册配置,非常方便而且便于调试。(使用xml配置,往往容易出现格式不对,或者其它问题,非常难于调试和排错)

2. 非常聪明,能够自动装配(发现构造函数需要的必须参数的时候,会自己想办法解决)

 

相关源代码下载: AutofactDemo.zip

 

相关文章:

IoC容器Autofac(1) -- 什么是IoC以及理解为什么要使用Ioc

IoC容器Autofac(3) - 理解Autofac原理,我实现的部分Autofac功能(附源码)

IoC容器Autofac(4) - Autofact + Asp.net MVC + EF Code First(附源码)

理解为什么要使用Ioc

分析Autofac如何实现Controller的Ioc(Inversion of Control)