• IoC容器Autofac(2)


    上篇文章中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. 非常聪明,能够自动装配(发现构造函数需要的必须参数的时候,会自己想办法解决)

  • 相关阅读:
    烂泥:高负载均衡学习haproxy之TCP应用
    烂泥:高负载均衡学习haproxy之关键词介绍
    sqlpuls基本命令
    Oracle开机自启动
    centos6.5安装oracle11g_2
    centos7安装图片界面
    centos7安装activemq
    centos7删除自带openjdk
    centos7安装nexus私服2.14
    mysql优化记录
  • 原文地址:https://www.cnblogs.com/Freedom0221/p/12499244.html
Copyright © 2020-2023  润新知