上篇文章中(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. 非常聰明,能夠自動裝配(發現構造函數需要的必須參數的時候,會自己想辦法解決)
相關源代碼下載: AutofactDemo.zip
相關文章:
IoC容器Autofac(1) -- 什么是IoC以及理解為什么要使用Ioc
IoC容器Autofac(3) - 理解Autofac原理,我實現的部分Autofac功能(附源碼)
IoC容器Autofac(4) - Autofact + Asp.net MVC + EF Code First(附源碼)