上一篇搞清楚了Autofac是什么东东后,这篇我们就开始说一下他怎么用,Autofac最大的优点就是他太简单方便了,既可以用配置文件,也可以用代码来装配。
Autofac的装配工作主要是分三步:
- 创建一个ContainerBuilder,容器构建器。
- 登记服务和组件,就是程序中所用到的实现了指定接口的类。
- 注册实例对象,就是把一些实例注册进行,Autofac在用的时候会自已过来拿
- 最后生成容器,Autofac会自动的检测依赖关系,然后进行自动装载。
- 最最后就是通过构造出来的IContainer取对象实例了。
弄清楚了步骤现在就看一下官方所给出的示例,范例有两个,一个是备忘录程序,一个是计算器(让我想起了曾经的一个面试题),现在先从备忘录开始吧,原文是英文的,如果英语好的同志直接看原版,英文一般的同志可以听我接下来的唠叨。
原文:http://www.codeproject.com/KB/architecture/di-with-autofac.aspx (英文)
源程序:http://www.codeproject.com/KB/architecture/di-with-autofac/autofac-example.zip
运行后的效果是:
看一下官方提供的类的关系图:
首先建一个控制台应用程序,工程名为Remember。偷个懒不截图了,大家都明白吧。现在展示一下这几个核心类是什么样以及它们的功能。
从上面的类图中我们可以粗略的看出,有一个Memo的类,当然它就是我们的主角(备忘录实体)
2 {
3 using System;
4 public class Memo
5 {
6 public DateTime DueAt { get; set; }
7 public string Title { get; set; }
8 }
9 }
MemoChecker顾名思意就是备忘录检查器,它是用来处理备忘录的工作者。
2 {
3 using System;
4 using System.Linq;
5
6 class MemoChecker
7 {
8 IQueryable<Memo> _memos;
9 IMemoDueNotifier _notifier;
10
11 public MemoChecker(IQueryable<Memo> memos, IMemoDueNotifier notifier)
12 {
13 this._memos = memos;
14 this._notifier = notifier;
15 }
16
17 public void CheckNow()
18 {
19 var overdueMemos = _memos.Where(
20 memo => memo.DueAt < DateTime.Now);
21
22 foreach (var memo in overdueMemos)
23 _notifier.MemoIsDue(memo);
24 }
25 }
26 }
还有一个接口IMemoDueNotifier,它是提醒者,该接口被备忘录检查器调用,以便在检查到过期的备忘录实体时提示。
2 {
3 interface IMemoDueNotifier
4 {
5 void MemoIsDue(Memo memo);
6 }
7 }
具体的提示方式则是由实现了IMemoDueNotifier的具体类来完成的,它们之间的契约是必须继承了TextWriter的类对能作为输出源(比如Console.Out输出到屏幕,StringWriter输出到字符串中,HttpWriter输出到Response.Write流等等),在该示例中实现了PrintingNotifier类来向屏幕中输出过期的备忘录实体。
2 {
3 using System.IO;
4 public class PrintingNotifier : IMemoDueNotifier
5 {
6 TextWriter _writer;
7 public PrintingNotifier(TextWriter writer)
8 {
9 _writer = writer;
10 }
11
12 public void MemoIsDue(Memo memo)
13 {
14 _writer.WriteLine("Memo '{0}' is due!", memo.Title);
15 }
16 }
17 }
这些类都很简单,建议同志们打开VS,引入Autofac然后自己亲自在电脑上敲一遍可以加深印象。而不仅仅时看一遍就过去了,很容易忘记的。
好了现在我们把刚刚那几个类已经将这些东东都完成了,但是它们还不能一起配合着干起活来。
最后看一下主程序的代码吧,在这里我们将把这些类都组织起来让他们一起工作。在这里我写了一种传统的直接new的方式,也写了使用Autofac的方式,两个方式运行出来的结果是一样的。但是我们通过Autofac注册的方式来处理系统中的所有类和接口(也称组件和服务)时,它们变得很离散,之间都没有耦合,我们需要什么对象时只需要象本示例中一样调用container.Resolve<MemoChecker>() 这个Resolve方法既可。这样我们就可以灵活的替换多个类。
2 {
3 using System;
4 using System.IO;
5 using System.Linq;
6 using Autofac;
7
8 class Program
9 {
10 static IQueryable<Memo> memos = new[]{
11 new Memo{ Title="Release Autofac 1.0", DueAt = new DateTime(2007,12,14)},
12 new Memo{ Title="Write CodeProject Article", DueAt = DateTime.Now},
13 new Memo{ Title="Release Autofac 2.3", DueAt = new DateTime(2010,07,01)}
14 }.AsQueryable();
15
16 static void Main(string[] args)
17 {
18 /*
19 * 以传统的依赖高度耦合的方式创建对象,
20 IMemoDueNotifier memoDueNotifier = new PrintingNotifier(Console.Out);
21 MemoChecker chechker = new MemoChecker(memos, memoDueNotifier);
22 chechker.CheckNow();
23 */
24
25 //以IoC依赖注入方式创建对象
26 using (var container = RegisterContainer())
27 container.Resolve<MemoChecker>().CheckNow();
28
29 Console.ReadKey();
30 }
31
32 /// <summary>
33 /// 注册组件容器
34 /// </summary>
35 /// <returns></returns>
36 private static IContainer RegisterContainer()
37 {
38
39 //使用了 Autofac 的依赖注入后的方式
40 //创建构造器
41 var builder = new ContainerBuilder();
42
43 //登记MemoChecker组件
44 builder.Register(c => new MemoChecker(
45 c.Resolve<IQueryable<Memo>>(),
46 c.Resolve<IMemoDueNotifier>())
47 );
48
49 //登记PrintingNotifier组件
50 builder.Register(c => new PrintingNotifier(
51 c.Resolve<TextWriter>())
52 ).As<IMemoDueNotifier>();
53
54 //注册实例对象
55 builder.RegisterInstance(memos);
56 builder.RegisterInstance(Console.Out).As<TextWriter>().ExternallyOwned();
57
58 //检查依赖关系生成容器
59 return builder.Build();
60 }
61 }
62 }
完成了上面这个范例你有些茫然,为什么我们要用如此复杂的方式来获取对象呢?它为我们带来了什么便利呢?这个问题很纠结(其实我第一看设计模式的时候也有这种问题)。
引用官方的一段话:
IoC带给了我们一种新的方式解藕在客户代码中直接new的对象,同时它遵循面向接口编程的OO原理,让我们对接口操作而不对具体实现操作,当我们面对程序的变化的时候,就可以增加新的类,然后注册到系统中,而无须修改原来的类和客户代码中所有对该类的引用,这同时也符合了开闭原则。
说了这么多官话,简单的来说就是让我们不需要在程序发生变化的时候,查找每个引用,然后Ctrl+V,Ctrl+C的重复替换,同样我们也可以动态的改变程序的具体实现方式,就拿我们这个例子来说,我们可以实现个继承自IMemoDueNotifier接口的ASP.NET 通知者,这样我们就可以将结果输出到WEB上了。
明天再写计算器的讲解吧,在下一篇中将使用配置文件来进行注册,而不象本篇完全的代码注册。