一、 引言
早在2001年,我就着手開發一個ASP.NET線上消息闆應用程式WebForums.NET。其目的是建立一個基于ASP.NET的消息闆系統,而且該系統可以容易插入到一個現有網站中。建構這樣一個端對端應用程式的特别挑戰之一就是,要為客戶提供一種方式以便能夠把它內建到他們自己的系統中去。例如,一個線上論壇明顯需要使用某種資料存儲來存儲使用者資訊、論壇、回寄資訊等;但是,最好不要把客戶鎖定到一種特定的資料存儲中。也就是說,你不應該說,"我的應用程式必須使用微軟的SQL Server 2000";因為這樣的話,使用Oracle或Access的客戶怎麼會使用你的軟體呢?
另一個內建到客戶現有資料中的問題是,所有的線上論壇站點都提供使用者帳戶和一種建立新帳戶的方式。典型情況下,這被模組化為一個論壇架構(以一個資料庫中的Users表形式存在)。但是,客戶很可能已經有他們自己的擁有成千的使用者帳戶的資料庫表。或者,一個客戶可能想在一個内部網設定中使用該論壇,并且想使用活動目錄而不是某種資料庫表來認證和存儲使用者資訊。是以,當一個論壇軟體系統建立一個Users資料庫表并對其客戶說"這就是你存儲使用者的方式"時,那些已經擁有現有基礎結構和使用者資料的客戶很可能會疏遠這樣的軟體。
是以,當你使用一種"僵硬"的API建構一個系統時,會産生特别的挑戰。一種"僵硬"的API不是提供一種方式來定制邏輯而是寫死實作細節(例如,你必須使用SQL Server作為你的後端資料存儲,且在這個資料庫有一個Users表,并将在其中存儲所有的使用者資訊)。然而,通過使用提供者設計模式,你可以輕易地打破這種"僵硬"性。借助于提供者設計模式,系統架構師隻需要定義API,至于程式設計功能則由系統來提供。對于一個線上論壇應用程式來說,這可能包括一個具有例如Authenticate(username,password)和GetUserDetails(username)等方法的Users類。
提供者模型的優秀在于客戶實作方案可以指定一個系統應該使用的定制類。這種定制類必須實作系統的良好定義的API;但是,它允許無縫地插入任何定制實作。也就是說,一旦定義這個API,系統實作者可以建立一個使用SQL Server和一個Users表的預設的具體實作-大多數客戶可以直接使用之而不必要作任何修改。那些有定制需要的客戶(他們想使用Oracle或以另外一些方式存儲使用者資料)可以建立他們自己的類,該類提供必要的功能并且把它們插入到這些客戶的系統中。
其實,提供者設計模式被應用于整個ASP.NET 2.0實作中。當然,網上也存在一些如何在ASP.NET 1.x應用程式中使用這一功能的教程。
在本文中,我們将詳細探讨提供者模型并分析如何把它應用于ASP.NET 2.0開發中。
二、 打破"僵硬"的API實作
在我早期的WebForums.NET開發中,我認識到,這種"僵硬"的API實作将會成為一個問題。我的軟體設計目标之一就是:盡可能靈活且可定制,并且使使用者使用SQL Server,而且我的使用者資料模型實作應該看起來充其量隻是有些限制性。為了克服這些問題,我建構了一個包含下面兩部分的系統:
1. 一組定義了系統的核心功能的抽象基類;
2. 能夠在運作時刻動态地加載一個擴充抽象基類。具體地說,該代碼負責檢查包含一個<ConfigSetting>節(該節中給出要使用的類的完全限定名)的Web.config檔案。
借助于這一架構,我可以通過一系列抽象基類來定義系統的功能,并使用SQL Server 2000和Users表來提供這些類的具體實作。滿足這一配置的客戶可以隻管使用該應用程式,并且一切将工作良好,且不需要他們編寫一行代碼。然而,那些需要定制的開發者們可以通過建立他們自己的派生自适當的抽象基類的類來實作。通過簡單地把該程式集放到應用程式的/bin目錄并更新Web.config檔案,他們可以讓系統使用這個新類。具體地說,WebForums.NET發行中帶有一個抽象基類DataProvider,它清楚地列舉出了系統中的所有方法,類似如下:
public abstract class DataProvider
{
public abstract bool AuthenticateUser(string username,string password);
public abstract User GetUserInfo(string username);
...
public static DataProvider Instance()
}
AuthenticateUser(username,password)和GetUserInfo(username)方法是系統定義的許多方法中的兩個方法的代表。而靜态Instance()方法是該DataProvider類的主要實作;它包含檢查代表了WebForums.NET配置資訊(該資訊訓示系統要使用的類的全稱限定名)的Web.config檔案的代碼。然後,該方法使用反射(和緩沖)來建立該類的一個執行個體并且把它傳回到系統。
WebForums.NET發行中還帶有一個派生自DataProvider基類的SqlDataProvider類,這個類提供分類方法的具體實作。例如,SqlDataProvider的所有方法都可以操作存儲于一個SQL Server 2000資料庫中的資料;與使用者相關的方法可以與一個預定義的Users資料庫表一起工作。一個想改變後端功能的客戶可以建立他自己的派生自DataProvider的類,這些資訊都可以展示于Web.config檔案中(指明應該使用他們的定制類)。例如,WebForums.NET中的Web.config可能包括下列内容:
<WebForumsSettings>
<add key="DataProviderAssemblyPath" value="path" />
<add key="DataProviderClassName" value="Namespace.Classname" />
</WebForumsSettings>
預設情況下,這個設定資訊引用随同WebForums.NET一起發行的SqlDataProvider類。然而,如果一個客戶建立他自己的API實作,那麼他可以提供自己的類的細節,并且系統會自動地開始使用他的實作來建立預設實作。
借助于這一架構,使用WebForums.NET的頁面開發者可以使用如下所示的代碼來認證一個使用者:
if (DataProvider.Instance().AuthenticateUser(username,password))
//使用者被認證
else
//使用者名/密碼無效!
當調用DataProvider.Instance()方法時,上面的配置檔案被檢查并且傳回适當類的一個執行個體。如果客戶還沒有建立他們自己的實作的話,這将是預設的SqlDataProvider類;而如果他們已經實作的話,它将是他們自己的類。一旦DataProvider.Instance()方法傳回一個适當的提供者執行個體,我們就可以簡單地調用該API的成員(在這個例子中是AuthenticateUser())。
WebForums.NET提供者模型-一個早期的原型
相對于微軟建議使用的提供者模型,Andy的提供者模型含有一些不足。一方面,WebForums.NET中提供了單個抽象基類,所有的API定義都聚集在這個類中。其負面作用在于,如果一個客戶僅想定制系統的一小部分,例如使用者資訊的存儲方式,那麼他必須提供該系統中所有方法的實作。一種更好的方案是,為系統中的每一個邏輯實體建立一個抽象基類。例如,對于一個線上消息闆應用程式來說,它可能需要一些類,如UsersProvider,ForumsProvider,PostsProvider,等等。然而,在你提供給一個客戶的提供者數目之間也存在一個平衡問題。更多提供者允許更為細緻的系統定制,但是也會相應地提高要求的配置标記的數量。
另外,我已經展示了對WebForums.NET的提供者模型實作代碼的作了進一步改進,以便使其更相似于微軟在ASP.NET 2.0中所使用的代碼。我認為,Andy的想法應該是提供者模型的先驅,盡管微軟的提供者模型實作更為清晰且更強壯一些。
一方面,WebForums.NET在2002年三月為微軟所收購;另一方面,Rob Howard及其他人又在系統中加入了大量的新特征并且在ASP.NET論壇中以自由方式發行它。Today Rob及其小組成員已經把ASP.NET論壇變成了一個Community Server(它簡直把部落格、論壇、畫廊、清單伺服器、新聞閱讀器等全部融為一體)。今天,Andy所建立的概念與實作被廣泛應用于ASP.NET論壇和Community Server中,甚至被應用于許多核心ASP.NET 2.0元件中。
三、 提供者模型優點
提供者模型提供許多優點。首先,在代碼和後端實作之間存在清晰的分離。不管認證一個使用者的代碼是針對一個SQL Server 2000資料庫的Users表還是針對于一個活動目錄存儲,從頁面開發者的觀察看來代碼都是相同的:
DataProvider.Instance().AuthenticateUser(username,password);
而且,後端實作變化是透明的。
因為系統架構師被鼓勵建立預設的實作,是以提供者模型提供了兩種世界的最好結合:如果對預設實作已經比較滿意,那麼系統會按預期進行工作;對于需要定制系統的使用者來說,他們盡管定制好了而不必幹擾現有代碼或程式設計邏輯。這個設計模式也使得原型化和靈敏開發容易許多。例如,在早期系統使用階段,僅使用預設實作可能更容易。然而,以後你可能需要定制某些方面以便把該工作與你的公司的現有系統內建到一起。這時,你可以通過提供者模型實作需要的定制。這意味着,不需要改變你的早期工作來反映後端實作的變化。
就象許多好的設計模型一樣,提供者模型也提供了開發者之間的職責分離。這樣以來,一部分開發者可以使用他們精通的系統API進行工作,而另一部分開發者可以專注于後端實作和定制任務開發;而且,這兩組人員可以工作在同一個系統上而不會互相幹擾。而且,如果他們所使用的系統是一種工業标準(例如ASP.NET 2.0),那麼這兩類任務中的技能都可以被容易地移植到未來的工作中。
<b>四、 ASP.NET 2.0提供者模型</b>
ASP.NET 2.0在全部其架構中都利用提供者模型。例如,它的許多子系統-會員,站點導航,個性化等都利用了提供者模型技術。而且,每個子系統都提供一種預設實作,但也能使客戶定制其功能以滿足他們自己的需要。例如,ASP.NET 2.0的站點導航部分允許一個頁面開發者定義他們網站的可導航的結構。然後,這些資料就可以被各種Web控件所使用以便顯示站點地圖、樹狀視圖或菜單-它們能夠高亮站點的導航并且/或者顯示使用者的站點位置。除了與導航相關的Web控件外,站點導航API還提供了一組方法用于實作與網站導航資訊的互動。
預設地,站點的可導航資訊必須以一個正确格式化的XML檔案編碼。這種資料存儲方式是,預設站點導航被寫死使用。然而,ASP.NET 2.0的提供者模型可以使你更容易地使用你自己的資料存儲來實作站點導航。例如,在一個我目前開發的工程中,使用的一個資料庫包含站點中的頁面資訊以及不同的使用者擁有頁面中什麼樣的許可權。不是在一個XML檔案中重新定義這些資訊并且必須努力保持兩份資訊的及時更新;而是,通過利用ASP.NET 2.0中的站點導航功能,我可以簡單地建立一個提供者類,它能夠直接與資料庫資訊工作。一旦建立這個類并且在網站的配置中指定,導航Web控件就可以根據存儲在資料庫中的應用程式的定制導航資訊進行工作。(注意:在本文成文之時,這個工程使用的仍然是ASP.NET 1.x;然而,這個例子卻有希望能夠向你展示提供者模型的優點。)
作為個人,我想,提供者模型隻是ASP.NET 2.0提供的最優秀的遷移特征之一。ASP.NET 2.0提供了很多開發者在1.x版本中必須定制的新特征。如果這些2.0版本的新特征使用過于"僵硬"的實作方式,那麼它将阻止基于定制方案的正在使用中的1.x應用程式向它的遷移,因為許多新的ASP.NET 2.0 Web控件都使用了這些新的子系統。然而,有了提供者模型後,我們就可以把我們的1.x應用程式更新到2.0版本并且建立一個提供者以便使2.0版本的新的子系統與我們的定制方案內建到一起。這意味着,當遷移到2.0版本時,我們可以使用新的Web控件并且使它們通過提供者模型而自然地使用我們的現有系統。
<b>五、 補充資訊</b>
随着提供者模型成為ASP.NET 2.0中的一個重要組成部分,微軟出版了很多關于這個主題的文章。如果你想了解更多這方面的資訊,那麼我鼓勵你閱讀一下Rob Howard的兩篇文章:
· 《提供者模型設計模式與規範》;
· 《ASP.NET 1.x提供者模型》。
其中,第二篇文章分析了如何把提供者模型應用于你的ASP.NET 1.x應用程式。還有兩篇文章讨論了ASP.NET 2.0的站點導航子系統中的提供者用法:
· 《了解和擴充ASP.NET 2.0中的站點導航系統》,作者David Gristwood;
· 《定制ASP.NET 2.0中的提供者》,作者Morgan Skinner。
注意,微軟還發行了另外一個提供者開發工具包,它也用于建立ASP.NET 2.0提供者。而且,還有一篇不錯的文章《ASP.NET 2.0提供者模型》可供你參考。
<b>六、 結論</b>
當建立具有各種要求的大量客戶所使用的系統時,一種"僵硬"的API實作可能會"吓壞"了開發者;這種"僵硬"的實作往往會強迫客戶同意且被鎖定于系統架構師的"視野"之内。而一般地,公司往往更對能夠與他們的現有方案協同工作的應用程式和架構感興趣而不是強迫他們的方案服從供應商提供的系統。
提供者模型提供了一種打破這種"僵硬"的實作問題的方法。借助于提供者模型,系統就能夠靈活地使用擴充特定基類的任何類。這樣以來,客戶可以建立他們自己的包括他們的定制邏輯和業務規則的派生類。而且,這些新類可以無縫地插接到系統中,而不必幹擾應用程式中的現有代碼或任何自建立以來的新的定制代碼。
總之,提供者模型在ASP.NET 2.0中得到普通使用;而且這些概念也可以應用于ASP.NET 1.x應用程式中。
本文轉自朱先忠老師51CTO部落格,原文連結:http://blog.51cto.com/zhuxianzhong/59342 ,如需轉載請自行聯系原作者