1. 程式人生 > >深入淺出設計模式——工廠方法模式(Factory Method)

深入淺出設計模式——工廠方法模式(Factory Method)

轉自:https://www.cnblogs.com/Bobby0322/p/4179921.html

介紹
在簡單工廠模式中,我們提到,工廠方法模式是簡單工廠模式的一個延伸,它屬於Gof23中設計模式的建立型設計模式。它解決的仍然是軟體設計中與建立物件有關的問題。它可以更好的處理客戶的需求變化。

引入
我們繼續來說"new"的問題,我們在簡單工廠模式中,將例項化物件的工作推遲到了專門負責建立物件的工廠類中,這樣,在我們事先預知的情況下,可以根據我們的需要動態建立產品類。但是,我們的預知是有限的,客戶的變化可能是無限的。所以,就出現了問題,一旦客戶的變化超越了我們的預知,我們就必須修改我們的原始碼了。這是設計模式所不允許的,怎麼辦呢?工廠方法模式正是解決此類問題的。
問題:具體工廠類的建立工作不能滿足我們的要求了,建立的工作變化了
解決思路:哪裡變化,封裝哪裡。把具體工廠封裝起來。

定義
工廠方法模式又稱為工廠模式,也叫虛擬構造器(Virtual Constructor)模式或者多型工廠模式(Polymorphic Factory),在工廠方法模式中,父類負責定義建立物件的公共介面,而子類則負責生成具體的物件,這樣做的目的是將類的例項化操作延遲到子類中完成,即由子類來決定究竟應該例項化(建立)哪一個類。
工廠方式法模式(Factory Method),定義了一個用於建立物件的介面,讓子類決定例項化哪一個類。工廠方法使一個類的例項化延遲到子類。

意圖
定義一個使用者建立物件的介面,讓子類決定例項化哪一個類,工廠方法模式使一個類的例項化延遲到其子類。

參與者
抽象產品角色(Product):定義產品的介面
具體產品角色(ConcreteProduct) :實現介面Product的具體產品類
抽象工廠角色(Creator) :宣告工廠方法(FactoryMethod),返回一個產品
真實的工廠(ConcreteCreator):實現FactoryMethod工廠方法,由客戶呼叫,返回一個產品的例項
工廠方法模式UML圖


現實生活中的例子
為了方便大家理解,我仍然舉穿衣服方面的一個例子。這個例子與簡單工廠模式中的那個例子有些不同。
據說清朝有個皇帝穿衣非常的奢侈,每種衣服(具體產品類)由一宮女(具體工廠類)專門負責,這樣一來,每增加一種衣服(具體產品類),就要多出一個宮女(具體工廠類),但是他們各負其責,互不影響。皇帝之所以這樣做,是因為針對穿衣服這件事來說,可擴充套件性是非常強的。

分析
實現的功能:可以根據皇帝的要求,動態的建立(由宮女去拿)已存在的具體產品(衣服),如果皇帝的要求太苛刻,這種衣服還沒有,只需要增加一個宮女,一個衣服就能夠滿足他的要求了。每個宮女只負責一種衣服(高內聚),要增加一種衣服,對於以前的所有宮女與衣服來說,都不會受到影響(設計模式中所期望的)。說到這裡,是不是明白了工廠方法模式所能解決的問題及其應用了?呵呵。。你一定在想,比簡單工廠模式靈活性高吧。
體系結構


ICoat.cs

複製程式碼
namespace FactoryMethod
{
    /// <summary>
    /// 抽象產品類
    /// </summary>
    public interface ICoat
    {
        void ShowCoat();
    }
}
複製程式碼

IFactory.cs

複製程式碼
namespace FactoryMethod
{
    /// <summary>
    /// 抽象工廠類,定義產品的介面
    /// </summary>
    public interface IFactory
    {
        ICoat CreateCoat();
    }
}
複製程式碼

FashionCoat.cs

複製程式碼
using System;

namespace FactoryMethod
{
    /// <summary>
    /// 具體產品類,時尚上衣類
    /// </summary>
    public class FashionCoat :ICoat
    {
        public void ShowCoat()
        {
            Console.WriteLine("這件是時尚上衣");
        }
    }
}
複製程式碼

BusinessCoat.cs

複製程式碼
using System;

namespace FactoryMethod
{
    /// <summary>
    /// 具體產品類,商務上衣類
    /// </summary>
    public class BusinessCoat :ICoat
    {
        public void ShowCoat()
        {
            Console.WriteLine("這件是商務上衣");
        }
    }
}
複製程式碼

FashionFactory.cs

複製程式碼
namespace FactoryMethod
{
    /// <summary>
    /// 具體工廠類,用於建立時尚上衣
    /// </summary>
    public class FashionFactory :IFactory
    {
        public ICoat CreateCoat()
        {
            return new FashionCoat();
        }
    }
}
複製程式碼

BusinessFactory.cs

複製程式碼
namespace FactoryMethod
{
    /// <summary>
    /// 具體工廠類:用於建立商務上衣類
    /// </summary>
    public class BusinessFactory : IFactory
    {
        public ICoat CreateCoat()
        {
            return new BusinessCoat();
        }
    }
}
複製程式碼

App.config

複製程式碼
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <appSettings>
        <add key="FactoryName" value="FashionFactory"/>
    </appSettings>
</configuration>
複製程式碼

Program.cs

複製程式碼
using System;
using System.Configuration;
using System.Reflection;

namespace FactoryMethod
{
    class Client
    {
        static void Main(string[] args)
        {
            //BusinessFactory factory = new BusinessFactory();
            //為了方便以後修改,將工廠類的類名寫在應用程式配置檔案中
            string factoryName = ConfigurationManager.AppSettings["FactoryName"];
            IFactory factory = (IFactory)Assembly.Load("FactoryMethod").CreateInstance("FactoryMethod." + factoryName);
            
            ICoat coat = factory.CreateCoat();
            //顯示你要的上衣
            coat.ShowCoat();
            Console.ReadLine();
        }
    }
}
複製程式碼

客戶端程式碼需要注意的兩個地方:
1、把具體工廠類類名稱寫在了應用程式配置檔案中,方便修改;
2、用到了反射,利用.NET提供的反射可以根據類名來建立它的例項,非常方便。

工廠方法模式的優點
在工廠方法模式中,工廠方法用來建立客戶所需要的產品,同時還向客戶隱藏了哪種具體產品類將被例項化這一細節,使用者只需要關心所需產品對應的工廠,無須關心建立細節,甚至無須知道具體產品類的類名。
基於工廠角色和產品角色的多型性設計是工廠方法模式的關鍵。它能夠使工廠可以自主確定建立何種產品物件,而如何建立這個物件的細節則完全封裝在具體工廠內部。工廠方法模式之所以又被稱為多型工廠模式,是因為所有的具體工廠類都具有同一抽象父類。
使用工廠方法模式的另一個優點是在系統中加入新產品時,無須修改抽象工廠和抽象產品提供的介面,無須修改客戶端,也無須修改其他的具體工廠和具體產品,而只要新增一個具體工廠和具體產品就可以了。這樣,系統的可擴充套件性也就變得非常好,完全符合“開閉原則”。

工廠方法模式的缺點
在新增新產品時,需要編寫新的具體產品類,而且還要提供與之對應的具體工廠類,系統中類的個數將成對增加,在一定程度上增加了系統的複雜度,有更多的類需要編譯和執行,會給系統帶來一些額外的開銷。
由於考慮到系統的可擴充套件性,需要引入抽象層,在客戶端程式碼中均使用抽象層進行定義,增加了系統的抽象性和理解難度,且在實現時可能需要用到DOM、反射等技術,增加了系統的實現難度。

模式適用環境
在以下情況下可以使用工廠方法模式:
一個類不知道它所需要的物件的類:在工廠方法模式中,客戶端不需要知道具體產品類的類名,只需要知道所對應的工廠即可,具體的產品物件由具體工廠類建立;客戶端需要知道建立具體產品的工廠類。
一個類通過其子類來指定建立哪個物件:在工廠方法模式中,對於抽象工廠類只需要提供一個建立產品的介面,而由其子類來確定具體要建立的物件,利用面向物件的多型性和里氏代換原則,在程式執行時,子類物件將覆蓋父類物件,從而使得系統更容易擴充套件。
將建立物件的任務委託給多個工廠子類中的某一個,客戶端在使用時可以無須關心是哪一個工廠子類建立產品子類,需要時再動態指定,可將具體工廠類的類名儲存在配置檔案或資料庫中。

小結
工廠方法模式又稱為工廠模式,它屬於類建立型模式。在工廠方法模式中,工廠父類負責定義建立產品物件的公共介面,而工廠子類則負責生成具體的產品物件,這樣做的目的是將產品類的例項化操作延遲到工廠子類中完成,即通過工廠子類來確定究竟應該例項化哪一個具體產品類。
工廠方法模式包含四個角色:抽象產品是定義產品的介面,是工廠方法模式所建立物件的超型別,即產品物件的共同父類或介面;具體產品實現了抽象產品介面,某種型別的具體產品由專門的具體工廠建立,它們之間往往一一對應;抽象工廠中聲明瞭工廠方法,用於返回一個產品,它是工廠方法模式的核心,任何在模式中建立物件的工廠類都必須實現該介面;具體工廠是抽象工廠類的子類,實現了抽象工廠中定義的工廠方法,並可由客戶呼叫,返回一個具體產品類的例項。 
工廠方法模式是簡單工廠模式的進一步抽象和推廣。由於使用了面向物件的多型性,工廠方法模式保持了簡單工廠模式的優點,而且克服了它的缺點。在工廠方法模式中,核心的工廠類不再負責所有產品的建立,而是將具體建立工作交給子類去做。這個核心類僅僅負責給出具體工廠必須實現的介面,而不負責產品類被例項化這種細節,這使得工廠方法模式可以允許系統在不修改工廠角色的情況下引進新產品。 
工廠方法模式的主要優點是增加新的產品類時無須修改現有系統,並封裝了產品物件的建立細節,系統具有良好的靈活性和可擴充套件性;其缺點在於增加新產品的同時需要增加新的工廠,導致系統類的個數成對增加,在一定程度上增加了系統的複雜性。 
工廠方法模式適用情況包括:一個類不知道它所需要的物件的類;一個類通過其子類來指定建立哪個物件;將建立物件的任務委託給多個工廠子類中的某一個,客戶端在使用時可以無須關心是哪一個工廠子類建立產品子類,需要時再動態指定。