日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

.NET设计模式(5):工厂方法模式(Factory Method)

發(fā)布時(shí)間:2025/1/21 68 豆豆
生活随笔 收集整理的這篇文章主要介紹了 .NET设计模式(5):工厂方法模式(Factory Method) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

概述

在軟件系統(tǒng)中,經(jīng)常面臨著“某個(gè)對象”的創(chuàng)建工作,由于需求的變化,這個(gè)對象的具體實(shí)現(xiàn)經(jīng)常面臨著劇烈的變化,但是它卻擁有比較穩(wěn)定的接口。如何應(yīng)對這種變化?提供一種封裝機(jī)制來隔離出“這個(gè)易變對象”的變化,從而保持系統(tǒng)中“其它依賴該對象的對象”不隨著需求的改變而改變?這就是要說的Factory Method模式了。

意圖

定義一個(gè)用戶創(chuàng)建對象的接口,讓子類決定實(shí)例化哪一個(gè)類。Factory Method使一個(gè)類的實(shí)例化延遲到其子類。

結(jié)構(gòu)圖

生活中的例子

工廠方法定義一個(gè)用于創(chuàng)建對象的接口,但是讓子類決定實(shí)例化哪個(gè)類。壓注成型演示了這種模式。塑料玩具制造商加工塑料粉,將塑料注入到希望形狀的模具中。玩具的類別(車,人物等等)是由模具決定的。

工廠方法解說

在工廠方法模式中,核心的工廠類不再負(fù)責(zé)所有產(chǎn)品的創(chuàng)建,而是將具體創(chuàng)建工作交給子類去做。這個(gè)核心類僅僅負(fù)責(zé)給出具體工廠必須實(shí)現(xiàn)的接口,而不接觸哪一個(gè)產(chǎn)品類被實(shí)例化這種細(xì)節(jié)。這使得工廠方法模式可以允許系統(tǒng)在不修改工廠角色的情況下引進(jìn)新產(chǎn)品。在Factory Method模式中,工廠類與產(chǎn)品類往往具有平行的等級結(jié)構(gòu),它們之間一一對應(yīng)。

現(xiàn)在我們考慮一個(gè)日志記錄的例子(這里我們只是為了說明Factory Method模式,實(shí)際項(xiàng)目中的日志記錄不會這么去做,也要比這復(fù)雜一些)。假定我們要設(shè)計(jì)日志記錄的類,支持記錄的方法有FileLogEventLog兩種方式。在這里我們先不談設(shè)計(jì)模式,那么這個(gè)日志記錄的類就很好實(shí)現(xiàn)了:

?1///?<summary>
?2///?日志記錄類
?3///?</summary>

?4public?class?Log
?5????{
?6
?7????????public?void?WriteEvent()
?8????????{
?9????????????Console.WriteLine("EventLog?Success!");
10????????}

11????
12????????public?void?WriteFile()
13????????{
14????????????Console.WriteLine("FileLog?Success!");
15????????}

16
17????????public?void?Write(string?LogType)
18????????{
19????????????switch(LogType.ToLower())
20????????????{
21????????????????case?"event":
22????????????????????WriteEvent();
23????????????????????break;
24
25????????????????case?"file":
26????????????????????WriteFile();
27????????????????????break;
28
29????????????????default:
30????????????????????break;
31????????????}

32????????}

33????}

34

這樣的程序結(jié)構(gòu)顯然不能符合我們的要求,如果我們增加一種新的日志記錄的方式DatabaseLog,那就要修改Log類,隨著記錄方式的變化,switch語句在不斷的變化,這樣就引起了整個(gè)應(yīng)用程序的不穩(wěn)定,進(jìn)一步分析上面的代碼,發(fā)現(xiàn)對于EventLogFileLog是兩種完全不同的記錄方式,它們之間不應(yīng)該存在必然的聯(lián)系,而應(yīng)該把它們分別作為單獨(dú)的對象來對待。

?1///?<summary>
?2///?EventLog類
?3///?</summary>

?4public?class?EventLog
?5{
?6????public?void?Write()
?7????{
?8????????Console.WriteLine("EventLog?Write?Success!");
?9????}

10}

11
12///?<summary>
13///?FileLog類
14///?</summary>

15public?class?FileLog
16{
17????public?void?Write()
18????{
19????????Console.WriteLine("FileLog?Write?Success!");
20????}

21}

22

進(jìn)一步抽象,為它們抽象出一個(gè)共同的父類,結(jié)構(gòu)圖如下:

實(shí)現(xiàn)代碼:

1///?<summary>
2///?Log類
3///?</summary>

4public?abstract?class?Log
5{
6????public?abstract?void?Write();
7}

8

此時(shí)EventLog和FileLog類的代碼應(yīng)該如下:

?1///?<summary>
?2///?EventLog類
?3///?</summary>
?4public?class?EventLog:Log
?5{
?6????public?override?void?Write()
?7????{
?8????????Console.WriteLine("EventLog?Write?Success!");
?9????}

10}

11///?<summary>
12///?FileLog類
13///?</summary>

14public?class?FileLog:Log
15{
16????public?override?void?Write()
17????{
18????????Console.WriteLine("FileLog?Write?Success!");
19????}

20}

21

此時(shí)我們再看增加新的記錄日志方式DatabaseLog的時(shí)候,需要做哪些事情?只需要增加一個(gè)繼承父類Log的子類來實(shí)現(xiàn),而無需再去修改EventLogFileLog類,這樣的設(shè)計(jì)滿足了類之間的層次關(guān)系,又很好的符合了面向?qū)ο笤O(shè)計(jì)中的單一職責(zé)原則,每一個(gè)類都只負(fù)責(zé)一件具體的事情。到這里似乎我們的設(shè)計(jì)很完美了,事實(shí)上我們還沒有看客戶程序如何去調(diào)用。在應(yīng)用程序中,我們要使用某一種日志記錄方式,也許會用到如下這樣的語句:

EventLog?eventlog?=?new?EventLog();
eventlog.Write();

當(dāng)日志記錄的方式從EventLog變化為FileLog,我們就得修改所有程序代碼中出現(xiàn)上面語句的部分,這樣的工作量是可想而知的。此時(shí)就需要解耦具體的日志記錄方式和應(yīng)用程序。這就要引入Factory Method模式了,每一個(gè)日志記錄的對象就是工廠所生成的產(chǎn)品,既然有兩種記錄方式,那就需要兩個(gè)不同的工廠去生產(chǎn)了,代碼如下:

?1///?<summary>
?2///?EventFactory類
?3///?</summary>

?4public?class?EventFactory
?5{
?6????public?EventLog?Create()
?7????{
?8????????return?new?EventLog();
?9????}

10}

11///?<summary>
12///?FileFactory類
13///?</summary>

14public?class?FileFactory
15{
16????public?FileLog?Create()
17????{
18????????return?new?FileLog();
19????}

20}

21

這兩個(gè)工廠和具體的產(chǎn)品之間是平行的結(jié)構(gòu),并一一對應(yīng),并在它們的基礎(chǔ)上抽象出一個(gè)公用的接口,結(jié)構(gòu)圖如下:

實(shí)現(xiàn)代碼如下:

1///?<summary>
2///?LogFactory類
3///?</summary>

4public?abstract?class?LogFactory
5{
6????public?abstract?Log?Create();
7}

8

此時(shí)兩個(gè)具體工廠的代碼應(yīng)該如下:

?1///?<summary>
?2///?EventFactory類
?3///?</summary>
?4public?class?EventFactory:LogFactory
?5{
?6????public?override?EventLog?Create()
?7????{
?8????????return?new?EventLog();
?9????}

10}

11///?<summary>
12///?FileFactory類
13///?</summary>

14public?class?FileFactory:LogFactory
15{
16????public?override?FileLog?Create()
17????{
18????????return?new?FileLog();
19????}

20}

21

這樣通過工廠方法模式我們把上面那對象創(chuàng)建工作封裝在了工廠中,此時(shí)我們似乎完成了整個(gè)Factory Method的過程。這樣達(dá)到了我們應(yīng)用程序和具體日志記錄對象之間解耦的目的了嗎?看一下此時(shí)客戶端程序代碼:

?1///?<summary>
?2///?App類
?3///?</summary>
?4public?class?App
?5{
?6????public?static?void?Main(string[]?args)
?7????{
?8????????LogFactory?factory?=?new?EventFactory();
?9
10????????Log?log?=?factory.Create();
11
12????????log.Write();
13????}

14}

15

在客戶程序中,我們有效地避免了具體產(chǎn)品對象和應(yīng)用程序之間的耦合,可是我們也看到,增加了具體工廠對象和應(yīng)用程序之間的耦合。那這樣究竟帶來什么好處呢?我們知道,在應(yīng)用程序中,Log對象的創(chuàng)建是頻繁的,在這里我們可以把

LogFactory factory = new EventFactory();

這句話放在一個(gè)類模塊中,任何需要用到Log對象的地方仍然不變。要是換一種日志記錄方式,只要修改一處為:

LogFactory factory = new FileFactory();

其余的任何地方我們都不需要去修改。有人會說那還是修改代碼,其實(shí)在開發(fā)中我們很難避免修改,但是我們可以盡量做到只修改一處。

其實(shí)利用.NET的特性,我們可以避免這種不必要的修改。下面我們利用.NET中的反射機(jī)制來進(jìn)一步修改我們的程序,這時(shí)就要用到配置文件了,如果我們想使用哪一種日志記錄方式,則在相應(yīng)的配置文件中設(shè)置如下:

1<appSettings>
2????<add?key="factoryName"?value="EventFactory"></add>
3</appSettings>
4

此時(shí)客戶端代碼如下:

?1///?<summary>
?2///?App類
?3///?</summary>
?4public?class?App
?5{
?6????public?static?void?Main(string[]?args)
?7????{
?8????????string?strfactoryName?=?ConfigurationSettings.AppSettings["factoryName"];
?9????????
10????????LogFactory?factory;
11????????factory?=?(LogFactory)Assembly.Load("FactoryMethod").CreateInstance("FactoryMethod."?+?strfactoryName);
12
13????????Log?log?=?factory.Create();
14????????log.Write();
15????}

16}

17

現(xiàn)在我們看到,在引進(jìn)新產(chǎn)品(日志記錄方式)的情況下,我們并不需要去修改工廠類,而只是增加新的產(chǎn)品類和新的工廠類(注意:這是任何時(shí)候都不能避免的),這樣很好的符合了開放封閉原則。

ASP.NET HTTP通道中的應(yīng)用

Factory Method模式在ASP.NET HTTP通道中我們可以找到很多的例子。ASP.NET HTTP通道是System.Web命名空間下的一個(gè)類,WEB Server使用該類處理接收到的HTTP請求,并給客戶端發(fā)送響應(yīng)。HTTP通道主要的工作有Session管理,應(yīng)用程序池管理,緩存管理,安全等。

System.Web.HttpApplicationFactory

HttpRuntimeHTTP通道的入口點(diǎn),它根據(jù)每一個(gè)具體的請求創(chuàng)建一個(gè)HttpContext實(shí)例, HttpRuntime并沒有確定它將要處理請求的HttpApplication對象的類型,它調(diào)用了一個(gè)靜態(tài)的工廠方法HttpApplicationFactory.GetApplicationInstance,通過它來創(chuàng)建HttpContext實(shí)例。GetApplicationInstance使用HttpContext實(shí)例來確定針對這個(gè)請求該響應(yīng)哪個(gè)虛擬路徑,如果這個(gè)虛擬路徑以前請求過,HttpApplication(或者一個(gè)繼承于ASP.Global_asax的類的實(shí)例)將直接從應(yīng)用程序池中返回,否則針對該虛擬路徑將創(chuàng)建一個(gè)新的HttpApplication對象并返回。如下圖所示:

HttpApplicationFactory.GetApplicationInstance帶有一個(gè)類型為HttpContext的參數(shù),創(chuàng)建的所有對象(產(chǎn)品)都是HttpApplication的類型,通過反編譯,來看一下GetApplicationInstance的實(shí)現(xiàn):

?1internal?static?IHttpHandler?GetApplicationInstance(HttpContext?context)
?2{
?3??????if?(HttpApplicationFactory._customApplication?!=?null)
?4??????{
?5????????????return?HttpApplicationFactory._customApplication;
?6??????}

?7??????if?(HttpDebugHandler.IsDebuggingRequest(context))
?8??????{
?9????????????return?new?HttpDebugHandler();
10??????}

11??????if?(!HttpApplicationFactory._theApplicationFactory._inited)
12??????{
13????????????lock?(HttpApplicationFactory._theApplicationFactory)
14????????????{
15??????????????????if?(!HttpApplicationFactory._theApplicationFactory._inited)
16??????????????????{
17????????????????????????HttpApplicationFactory._theApplicationFactory.Init(context);
18????????????????????????HttpApplicationFactory._theApplicationFactory._inited?=?true;
19??????????????????}

20????????????}

21??????}

22??????return?HttpApplicationFactory._theApplicationFactory.GetNormalApplicationInstance(context);
23}

24

System.Web.IHttpHandlerFactory

我們來做進(jìn)一步的探索,HttpApplication實(shí)例需要一個(gè)Handler對象來處理資源請求, HttpApplication的主要任務(wù)就是找到真正處理請求的類。HttpApplication首先確定了一個(gè)創(chuàng)建Handler對象的工廠,來看一下在Machine.config文件中的配置區(qū)<httphandlers>,在配置文件注冊了應(yīng)用程序的具體處理類。例如在Machine.config中對*.aspx的處理將映射到System.Web.UI.PageHandlerFactory 類,而對*.ashx的處理將映射到System.Web.UI.SimpleHandlerFactory類,這兩個(gè)類都是繼承于IhttpHandlerFactory接口的具體類

<httpHandlers>

<add?verb="*"?path="*.aspx"?type="System.Web.UI.PageHandlerFactory"?/>

<add?verb="*"?path="*.ashx"?type="System.Web.UI.SimpleHandlerFactory"?/>



</httpHandlers>

這個(gè)配置區(qū)建立了資源請求的類型和處理請求的類之間的一個(gè)映射集。如果一個(gè).aspx頁面發(fā)出了請求,將會調(diào)用System.Web.UI.PageHandlerFactory類,HttpApplication調(diào)用接口IHttpHandlerFactory中的工廠方法GetHandler來創(chuàng)建一個(gè)Handler對象。當(dāng)一個(gè)名為sample.aspx的頁面發(fā)出請求時(shí),通過PageHandlerFactory將返回一個(gè)ASP.SamplePage_aspx對象(具體產(chǎn)品),如下圖:

IHttpHandlerFactory工廠:

1public?interface?IHttpHandlerFactory
2{
3??????//?Methods
4??????IHttpHandler?GetHandler(HttpContext?context,?string?requestType,?string?url,?string?pathTranslated);
5??????void?ReleaseHandler(IHttpHandler?handler);
6}

7

IHttpHandlerFactory.GetHandler是一個(gè)工廠方法模式的典型例子,在這個(gè)應(yīng)用中,各個(gè)角色的設(shè)置如下:

抽象工廠角色:IHttpHandlerFactory

具體工廠角色:PageHandlerFactory

抽象產(chǎn)品角色:IHttpHandler

具體產(chǎn)品角色:ASP.SamplePage_aspx

進(jìn)一步去理解

理解上面所說的之后,我們就可以去自定義工廠類來對特定的資源類型進(jìn)行處理。第一步我們需要?jiǎng)?chuàng)建兩個(gè)類去分別實(shí)現(xiàn)IHttpHandlerFactoryIHttpHandler這兩個(gè)接口。

?1public?class?HttpHandlerFactoryImpl:IHttpHandlerFactory?{
?2???
?3???IHttpHandler?IHttpHandlerFactory.GetHandler(
?4??????HttpContext?context,?String?requestType,?
?5??????String?url,?String?pathTranslated?)?{
?6
?7?????????return?new?HttpHandlerImpl();
?8?????????
?9???}
//IHttpHandlerFactory.GetHandler
10
11???void?IHttpHandlerFactory.ReleaseHandler(
12??????IHttpHandler?handler)?{?/*no-op*/?}
13
14}
//HttpHandlerFactoryImpl
15
16public?class?HttpHandlerImpl:IHttpHandler?{
17
18???void?IHttpHandler.ProcessRequest(HttpContext?context)?{
19??????
20??????context.Response.Write("sample?handler?invoked");
21??????
22???}
//ProcessRequest
23
24???bool?IHttpHandler.IsReusable?{?get?{?return?false;?}?}
25
26}
//HttpHandlerImpl
27

第二步需要在配置文件中建立資源請求類型和處理程序之間的映射。我們希望當(dāng)請求的類型為*.sample時(shí)進(jìn)入我們自定義的處理程序,如下:

<httpHandlers>

???
<add?verb="*"?path="*.sample"?

??????type
="HttpHandlerFactoryImpl,SampleHandler"?/>

</httpHandlers>

最后一步我們需要把文件擴(kuò)展*.sample映射到ASP.NET ISAPI擴(kuò)展DLLaspnet_isapi.dll)上。由于我們已經(jīng)建立了用于處理新擴(kuò)展文件的處理程序了,我們還需要把這個(gè)擴(kuò)展名告訴IIS并把它映射到ASP.NET。如果你不執(zhí)行這個(gè)步驟而試圖訪問*.sample文件,IIS將簡單地返回該文件而不是把它傳遞給ASP.NET運(yùn)行時(shí)。其結(jié)果是該HTTP處理程序不會被調(diào)用。

運(yùn)行Internet服務(wù)管理器,右鍵點(diǎn)擊默認(rèn)Web站點(diǎn),選擇屬性,移動到主目錄選項(xiàng)頁,并點(diǎn)擊配置按鈕。應(yīng)用程序配置對話框彈出來了。點(diǎn)擊添加按鈕并在可執(zhí)行字段輸入aspnet_isapi.dll文件路徑,在擴(kuò)展字段輸入.sample。其它字段不用處理;該對話框如下所示:

.NET Framework中,關(guān)于工廠模式的使用有很多的例子,例如IEnumerableIEnumerator就是一個(gè)Creator和一個(gè)ProductSystem.Security.Cryptography中關(guān)于加密算法的選擇,SymmetricAlgorithm, AsymmetricAlgorithm, HashAlgorithm分別是三個(gè)工廠,他們各有一個(gè)靜態(tài)的工廠方法CreateSystem.Net.WebRequest .NET Framework 的用于訪問 Internet 數(shù)據(jù)的請求/響應(yīng)模型的抽象基類。使用該請求/響應(yīng)模型的應(yīng)用程序可以用協(xié)議不可知的方式從 Internet 請求數(shù)據(jù)。在這種方式下,應(yīng)用程序處理 WebRequest 類的實(shí)例,而協(xié)議特定的子類則執(zhí)行請求的具體細(xì)節(jié)。請求從應(yīng)用程序發(fā)送到某個(gè)特定的 URI,如服務(wù)器上的 Web 頁。URI 從一個(gè)為應(yīng)用程序注冊的 WebRequest子代列表中確定要?jiǎng)?chuàng)建的適當(dāng)子類。注冊 WebRequest子代通常是為了處理某個(gè)特定的協(xié)議(如 HTTP FTP),但是也可以注冊它以處理對特定服務(wù)器或服務(wù)器上的路徑的請求。有時(shí)間我會就.NET Framework中工廠模式的使用作一個(gè)專題總結(jié)。

實(shí)現(xiàn)要點(diǎn)

1.?Factory Method模式的兩種情況:一是Creator類是一個(gè)抽象類且它不提供它所聲明的工廠方法的實(shí)現(xiàn);二是Creator是一個(gè)具體的類且它提供一個(gè)工廠方法的缺省實(shí)現(xiàn)。

2.?工廠方法是可以帶參數(shù)的。

3.?工廠的作用并不僅僅只是創(chuàng)建一個(gè)對象,它還可以做對象的初始化,參數(shù)的設(shè)置等。

效果

1.?用工廠方法在一個(gè)類的內(nèi)部創(chuàng)建對象通常比直接創(chuàng)建對象更靈活。

2.?Factory Method模式通過面向?qū)ο蟮氖址?#xff0c;將所要?jiǎng)?chuàng)建的具體對象的創(chuàng)建工作延遲到了子類,從而提供了一種擴(kuò)展的策略,較好的解決了這種緊耦合的關(guān)系。

適用性

在以下情況下,適用于工廠方法模式:

1.?????? 當(dāng)一個(gè)類不知道它所必須創(chuàng)建的對象的類的時(shí)候。

2.?????? 當(dāng)一個(gè)類希望由它的子類來指定它所創(chuàng)建的對象的時(shí)候。

3.?????? 當(dāng)類將創(chuàng)建對象的職責(zé)委托給多個(gè)幫助子類中的某一個(gè),并且你希望將哪一個(gè)幫助子類是代理者這一信息局部化的時(shí)候。

總結(jié)

Factory Method模式是設(shè)計(jì)模式中應(yīng)用最為廣泛的模式,通過本文,相信讀者已經(jīng)對它有了一定的認(rèn)識。然而我們要明確的是:在面向?qū)ο蟮木幊讨?#xff0c;對象的創(chuàng)建工作非常簡單,對象的創(chuàng)建時(shí)機(jī)卻很重要。Factory Method要解決的就是對象的創(chuàng)建時(shí)機(jī)問題,它提供了一種擴(kuò)展的策略,很好地符合了開放封閉原則。__________________________________________________________________________________

參考文獻(xiàn):

《設(shè)計(jì)模式》(中文版)

MSDN:《Exploring the Factory Design Pattern》

《DesignPatternsExplained》

轉(zhuǎn)載于:https://www.cnblogs.com/Aioria0622/archive/2007/11/21/967903.html

總結(jié)

以上是生活随笔為你收集整理的.NET设计模式(5):工厂方法模式(Factory Method)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。