回顾之前的代码
//这个类的作用是筛选出MPG类型的电影 public class MPGMovieLister { 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() }
在应用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改造上面的代码。
第一步: 从Nuget中添加Autofac引用
第二步:
* 创建一个ContainerBuilder对象(ContainerBuilder从字面的意思就是用来创建Container(容器)的,而Conainter就是我们从中取各种我们需要对象的地方)
* 注册我们后面将从容器中取出对象的类型。
代码是这样:
var builder = new ContainerBuilder(); builder.RegisterType<ListMovieFinder>().As<IMovieFinder>();//注册ListMovieFinder类型 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();
上面的例子中类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();
常见的程序架构大概是: UI层, 业务逻辑层, 持久层(数据层)。
我们可用Autofac作为不同层之间的中间人,让UI层依赖于业务逻辑层的抽象接口,业务逻辑层依赖于持久层的接口,而实际运行过程中的实例都由Auotfac来提供。
这样就能够解除不同层之间的依赖,将所有的注册类型的操作在一个核心函数或者核心类中实现,那么只要修改这个函数或者类就能够非常方便的让它们之间的依赖关系发生变化。
比如 在一个大的项目中,持久层和业务逻辑层是并行开发的,而且是不同团队开发,这个时候业务逻辑开发团队的人在没有持久层代码的情况下,如何开始呢?
我们只要定义好持久层的接口, 业务逻辑团队再写一些Stub类(桩类)来实现这些接口,让这些Stub类来替换真正的持久层,所要做的就只是简单的把这些Stub类型注册到Autofac中就可以了。同时做业务逻辑层的单元测试也非常容易了。
从上面的例子可以看出,使用IoC对于复杂的项目来说,非常有意义,能够为我们搭建一个好的开发层次。
同时,在使用过程中,还能够发现Autofac有以下优点:
1. 可以使用C#代码来完成注册配置,非常方便而且便于调试。(使用xml配置,往往容易出现格式不对,或者其它问题,非常难于调试和排错)
2. 非常聪明,能够自动装配(发现构造函数需要的必须参数的时候,会自己想办法解决)