首页 > 代码库 > 1、初识Autofac

1、初识Autofac

      近一两年写了很多小Web系统,逐渐开始变得熟练。现在最困扰我的,并不是某种具体需求如何去实现,而是如何更为优雅的规划整个应用程序。尽量降低不同的层之间的耦合,提高维护性和扩展性。而这种低耦合,基于接口的开发模式也恰好是应用很多先进开发手段的基础,诸如单元测试,TDD等。扯远了,还是从一个吃饭的例子说起。请原谅我是一个吃货...
      新建一个控制台应用程序:

namespace DEMO{    /// <summary>    /// 吃饭1.0版    /// </summary>   public class Dinner    {       public void HotPot()       {           Console.WriteLine("重庆火锅...");       }    }  public  class Person    {      private Dinner m_Dinner;      public Person()      {          m_Dinner = new Dinner();      }      public void Dinner()      {          m_Dinner.HotPot();      }    }}

       (此处省略了客户端代码),有一个人类,持有一个吃饭类的引用,在人类构造函数中初始化吃饭类并调用吃饭类的方法。此时“人"和”吃饭"发生了硬耦合,如果我某天想吃烧烤,只能在吃饭类中新增一个烧烤方法并修改人类。老鸟会告诉你,你可以通过接口来降低耦合度。so,我们来改一改代码。

namespace DEMO{    /// <summary>    /// 吃饭2.0版    /// </summary>    public interface IDinner    {        void Eating();    }    public class HotPot:IDinner    {        public void Eating()        {            Console.WriteLine("重庆火锅...");        }    }  public  class Person    {      private IDinner m_IDinner;      public Person()      {          m_IDinner = new HotPot();      }      public void Dinner()      {          m_IDinner.Eating();      }    }}

      创建一个吃饭接口,调用吃饭接口的方法,如果要更改实现,就更改接口的初始化语句,并添加相应的实现即可。看起来好像高端了那么一点点,利用了接口的多态。人类的构造函数里的这句 m_IDinner = new HotPot(); 一直是让我想不明白的地方。接口总要实例化到某个具体的对象上,如人类事先已经知道HotPot火锅类实现了吃饭接口,那这种初始化纯属脱裤子放屁多此一举,我直接使用火锅类不就得了吗?还费劲巴拉的弄个接口干什么呢? 换句话说,这种指定应该在人类的外部去做,人类本身并不应该去承担这种职责,人类就知道吃饭,至于吃什么,那是另外一个职责,应该分离出来。
      这种困惑并不是我独有的,为此有很多高手想了很多办法来解决,就是目前所说的IOC框架,IOC是控制反转的简写,就是把这种初始化或者new的职责,从类的内部转移到类的外部,一般是在 app_start的地方进行统一的初始化。解除了类的耦合,从而真正实现面向接口编程。.NET平台上目前也涌现了一大批IOC框架,目前最主流的就是Autofac。闲话少说,直接改代码:
      首先引入Autofac,你可以手动引入DLL或者直接用NUGET安装,VS下当然推荐NUGET了,写一句控制命令就搞定了:PM> install-package autofac ,这次贴出全部代码:  

namespace DEMO{    /// <summary>    /// 吃饭3.0版    /// </summary>    public interface IDinner    {        void Eating();    }    public class HotPot : IDinner    {        public void Eating()        {            Console.WriteLine("重庆火锅...");        }    }    public class Person    {        readonly IDinner m_IDinner;                public Person(IDinner idinner)        {            m_IDinner = idinner;        }        public void Dinner()        {            m_IDinner.Eating();        }    }    class Program    {        static void Main(string[] args)        {            var builder = new ContainerBuilder();            builder.RegisterType<Person>();            builder.RegisterType<HotPot>().As<IDinner>();            using (var container = builder.Build())            {                var person = container.Resolve<Person>();                person.Dinner();            }            Console.Read();        }    }}

        Person的构造带一个IDinner参数,也就是说在初始化的时候必须赋它一个IDinner实例。这个构造函数是写给autofac使用的,autofac会自动初始化IDinner的一个实例并传给Person的构造函数。这样一来,Person类中没有了类似 m_IDinner = new HotPot();这样的代码。Person类依赖接口IDinner,而接口是构造函数中由外部注入的,这就是“构造依赖注入”,如此一来,Person类和火锅类彻底没有了关系。从而实现了彻底解耦合。
       var builder = new ContainerBuilder(); //创建一个容器建造器
       builder.RegisterType<Person>();//将这个类型注册到autofac,
       builder.RegisterType<HotPot>().As<IDinner>();//将HotPot类作为IDinner的实例注册
       var person = container.Resolve<Person>();//Resolve可以理解为从容器中实例化一个对象出来,在调用Person的构造函数时,antofac会自动实例化一个HotPot对象并作为参数。

       上述的东西都是一些API的用法,看看就明白。
       如果我要更换实现,比如吃烧烤,就写一个烧烤实现,再更改一下注册类型即可,Person类完全不用动。在main函数中写了一大堆代码才实现了解耦,也许有人会觉得得不偿失,在网站开发的过程中,DAL,BLL,UI只有实现解耦合,才能实现真正的分层协作开发。以上述代码为例,IDinner接口的实现由另一个人来写,可能他还没写好,你此时可以自己写一个”桩“类实现IDinner接口,然后写代码即可,不必操心他的开发进度或者Bug会干扰到你。只需要注册一次即可,是不是很犀利?
       此外autofac还对asp.net mvc提供了良好的集成,有专门的类库用于mvc开发。
       以上是我刚刚接触autofac的一些浅见。    

1、初识Autofac