乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      博客園 - 探索設(shè)計模式(五):工廠方法模式(Factory Method)

       心之所指 2006-01-23

      工廠方法模式(Factory Method

      ——探索設(shè)計模式系列之五

      Terrylee200412

      概述

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

      意圖

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

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

      生活中的例子

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

      工廠方法解說

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

      現(xiàn)在我們考慮一個日志記錄的例子(這里我們只是為了說明Factory Method模式,實際項目中的日志記錄不會這么去做,也要比這復(fù)雜一些)。假定我們要設(shè)計日志記錄的類,支持記錄的方法有FileLogEventLog兩種方式。在這里我們先不談設(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語句在不斷的變化,這樣就引起了整個應(yīng)用程序的不穩(wěn)定,進一步分析上面的代碼,發(fā)現(xiàn)對于EventLogFileLog是兩種完全不同的記錄方式,它們之間不應(yīng)該存在必然的聯(lián)系,而應(yīng)該把它們分別作為單獨的對象來對待。

       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

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

      實現(xiàn)代碼:

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

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

      8

      此時EventLogFileLog類的代碼應(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

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

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

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

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

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

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

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

      8

      此時兩個具體工廠的代碼應(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)建工作封裝在了工廠中,此時我們似乎完成了整個Factory Method的過程。這樣達到了我們應(yīng)用程序和具體日志記錄對象之間解耦的目的了嗎?看一下此時客戶端程序代碼:

       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();

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

      LogFactory factory = new FileFactory();

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

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

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

      此時客戶端代碼如下:

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

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

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

      System.Web.HttpApplicationFactory

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

      HttpApplicationFactory.GetApplicationInstance帶有一個類型為HttpContext的參數(shù),創(chuàng)建的所有對象(產(chǎn)品)都是HttpApplication的類型,通過反編譯,來看一下GetApplicationInstance的實現(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

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

      <httpHandlers>

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

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



      </httpHandlers>

      這個配置區(qū)建立了資源請求的類型和處理請求的類之間的一個映射集。如果一個.aspx頁面發(fā)出了請求,將會調(diào)用System.Web.UI.PageHandlerFactory類,HttpApplication調(diào)用接口IHttpHandlerFactory中的工廠方法GetHandler來創(chuàng)建一個Handler對象。當(dāng)一個名為sample.aspx的頁面發(fā)出請求時,通過PageHandlerFactory將返回一個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是一個工廠方法模式的典型例子,在這個應(yīng)用中,各個角色的設(shè)置如下:

      抽象工廠角色:IHttpHandlerFactory

      具體工廠角色:PageHandlerFactory

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

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

      進一步去理解

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

       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時進入我們自定義的處理程序,如下:

      <httpHandlers>

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

            type
      ="HttpHandlerFactoryImpl,SampleHandler" />

      </httpHandlers>

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

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

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

      實現(xiàn)要點

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

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

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

      效果

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

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

      適用性

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

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

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

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

      總結(jié)

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

      參考文獻:

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

      MSDN:《Exploring the Factory Design Pattern

      DesignPatternsExplained

        本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
        轉(zhuǎn)藏 分享 獻花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多