标签:
近一两年写了很多小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的一些浅见。
标签:
原文地址:http://www.cnblogs.com/echoshinian100/p/4236270.html