IoC容器Autofac(2) - 一個簡單示例(附demo原始碼)
上篇文章中(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有以下優點:
可以使用C#程式碼來完成註冊配置,非常方便而且便於除錯。(使用xml配置,往往容易出現格式不對,或者其它問題,非常難於除錯和排錯)
非常聰明,能夠自動裝配(發現建構函式需要的必須引數的時候,會自己想辦法解決)
程式碼地址
相關文章
- 控制反轉(IOC容器)-Autofac入門
- 幾句程式碼簡單實現IoC容器
- spring原始碼解析之IOC容器(一)Spring原始碼
- Spring原始碼閱讀-IoC容器解析Spring原始碼
- Spring IOC容器核心流程原始碼分析Spring原始碼
- Spring 原始碼 (2)Spring IOC 容器 前戲準備工作Spring原始碼
- WebGL簡易教程(一):第一個簡單示例Web
- 寫一個簡單的demo理解vuexVue
- Spring原始碼分析(三)手寫簡單的IOC容器和解決迴圈依賴問題Spring原始碼
- 手寫一個最簡單的IOC容器,從而瞭解spring的核心原理Spring
- spring-IOC容器原始碼分析(一)bean初始化流程Spring原始碼Bean
- Spring5原始碼解析系列一——IoC容器核心類圖Spring原始碼
- Vue.js SSR Step by Step (2) – 一個簡單的同構DEMOVue.js
- Vue.js SSR Step by Step (2) - 一個簡單的同構DEMOVue.js
- 從原始碼看Spring中IOC容器的實現(二):IOC容器的初始化原始碼Spring
- 造輪子:實現一個簡易的 Spring IoC 容器Spring
- 自己動手實現一個簡單的 IOC
- 一個最簡單的WebSocket hello world demoWeb
- 關於SSM框架的一個簡單DemoSSM框架
- 一個簡單的介面測試框架 demo框架
- [譯]ViewModels:一個簡單的示例View
- 一個簡單的狀態列示例
- 一個簡單的 indexedDB 應用示例Index
- 寫一個簡單的IoC容器案例,理解什麼是依賴注入和控制反轉依賴注入
- Spring IOC 容器預啟動流程原始碼探析Spring原始碼
- 一個簡單案例的Vue2.0原始碼Vue原始碼
- Promise 原始碼:實現一個簡單的 PromisePromise原始碼
- Managed DirectX中的DirectShow應用(簡單Demo及原始碼)原始碼
- koa2原始碼解讀及實現一個簡單的koa2框架原始碼框架
- 寫一個簡單的 Facade 示例
- Spring IoC容器初始化 — Resource定位原始碼分析Spring原始碼
- spring原始碼解析之IOC容器(四)——屬性注入Spring原始碼
- spring原始碼解析之IOC容器(三)——依賴注入Spring原始碼依賴注入
- Spring原始碼分析:Spring IOC容器初始化Spring原始碼
- 從原始碼看Spring中IOC容器的實現(一):介面體系原始碼Spring
- vuex 原始碼:如何實現一個簡單的 vuexVue原始碼
- JS編寫簡單的彈窗外掛(含有demo和原始碼)JS原始碼
- Spring原始碼之IOC(一)BeanDefinition原始碼解析Spring原始碼Bean