工廠方法模式(Factory Method Pattern)也稱爲工廠模式,又稱爲虛擬構造器模式或多態模式。html
在工廠方法模式中,工廠父類負責定義建立產品對象的公共接口,而工廠子類則負責生成具體的產品對象,這樣作的目的是將產品類的實例化操做延遲到工廠子類中完成,即經過工廠子類來肯定究竟應該實例化哪個具體產品類。git
工廠方法模式主要類:github
Product抽象產品類(或是接口),派生出全部的具體產品類ConcreteProductA
、ConcreteProductA
……數據庫
ConcreteProduct 具體產品類,繼承於Product
抽象類設計模式
Factory 抽象工廠接口,全部的具體工廠類都是實現該接口框架
ConcreteFactory 具體工廠,實現了Factory
接口,建立具體的產品對象ide
在設計模式——簡單工廠模式一文中,使用簡單工廠模式建立了一個簡單的四則計算器,工具
咱們須要一個抽象的Operation父類,派生出四個加減乘除子類設計
在工廠中根據傳入的符號參數,生成不一樣的運行類。日誌
可是問題也就來了,如果添加一個新的運行類,咱們依舊能夠繼承於Operation抽象父類,override運算方法,而後在工廠中添加一個新的分支,建立該運算類。
那麼問題就來了!按照設計原則——開閉原則,咱們應該開放擴展,關閉修改。在這裏,咱們擴展了運算方法,可是同時也對Factory類進行了修改,這明顯不符合開閉原則啊!
其實這就是簡單工廠模式的缺點。
爲了解決這個缺點,咱們引入工廠方法模式。(事實上,簡單工廠模式是工廠方法模式的簡化版,而不是由於簡單工廠模式有缺點才演變爲工廠方法模式的)
咱們對在簡單工廠模式中實現的計算器進行進一步的重構
提取出工廠接口:IFactory
,分別建立每一個運行類的工廠類AddFactory
,SubFactory
,MulFactory
,DivFactory
。
//工廠接口:抽象工廠 public interface IFactory { Operation CreateOperation(); } //加法運行工廠:具體工廠 public class AddFactory : IFactory { public Operation CreateOperation() { return new OperationAdd(); } } //減法運行工廠:具體工廠 public class SubFactory : IFactory { public Operation CreateOperation() { return new OperationSub(); } } //乘法運行工廠:具體工廠 public class MulFactory : IFactory { public Operation CreateOperation() { return new OperationMul(); } } //除法運行工廠:具體工廠 public class DivFactory : IFactory { public Operation CreateOperation() { return new OperationDiv(); } }
這時咱們在客戶端,能夠這樣使用:
static void Main(string[] args) { //建立一個具體的工廠對象:加法工廠對象 IFactory addFactory = new AddFactory(); //使用加法工廠對象建立加法運算類 Operation addOper = addFactory.CreateOperation(); addOper.NumA = 2; addOper.NumB = 3; Console.WriteLine(addOper.GetResult());//print:5 Console.ReadKey(); }
其實到這裏是能夠發現,通過重構後的代碼,每一個具體運算類都有一個相應的工廠類
如果須要添加一個新的運行符,則咱們只須要建立一個新的具體工廠類XXXFactory
,實現抽象工廠接口IFactory
,同時再建立新的運算符實現類XXX
,繼承於運算抽象父類Operation
。這樣就能夠在不修改程序中的現有的類的情形下,實現對程序的擴展!知足了開閉原則,避免了簡單工廠模式的缺點!
工廠方法模式實現時,客戶端須要決定實例化哪個工廠來實現運算類,選擇判斷的問題仍是存在的,也就是說,工廠方法把簡單工廠的內部邏輯判斷移到了客戶端代碼來進行。你想要加功能,原本是改工廠類的,而如今是修改客戶端
示例來源於《設計模式實訓-第二版》
某系統日誌記錄器要求支持多種日誌記錄方式,如文件日誌記錄(FileLog)、數據庫日誌記錄(DatabaseLog)等,且用戶能夠根據要求動態選擇日誌記錄方式,現使用工廠方法模式設計該系統。
這裏我爲何要用這個示例? 由於在實際開發中,一些框架和程序包都是按照工廠方法模式開發的,包括日誌框架。
其實你想想,使用的一些框架和程序包的調用,是否是都先是建立一個ConcreteFactory對象(或者Creator對象),以後使用該具體工廠對象建立ConcreteProduct對象
①抽象產品和具體產品的實現代碼
//抽象產品:日誌記錄器總接口(使用抽象類也能夠) public interface ILog { void WriteLog(); } //具體產品:文件日誌記錄器 public class FileLog : ILog { public void WriteLog() { Console.WriteLine("記錄日誌於日誌文件中"); } } //具體產品:數據庫日誌記錄器 public class DatabaseLog : ILog { public void WriteLog() { Console.WriteLine("記錄日誌於日誌數據庫中"); } }
②抽象工廠和具體工廠的實現代碼
//抽象工廠:日誌記錄器工廠 public interface ILogFactory { ILog CreateLog(); } //具體工廠:文件日記記錄器工廠 public class FileLogFactory : ILogFactory { public ILog CreateLog() { return new FileLog(); } } //具體工廠:數據庫日記記錄器工廠 public class DatabaseLogFactory : ILogFactory { public ILog CreateLog() { return new DatabaseLog(); } }
③客戶端代碼
class Program { static void Main(string[] args) { //建立一個具體的工廠對象:FileLogFactory ILogFactory logFac = new FileLogFactory(); //由工廠對象,建立產品對象:FileLog //FileLog log = logFac.CreateLog() as FileLog; ILog log = logFac.CreateLog(); log.WriteLog();//print:記日誌於日誌文件中 Console.ReadKey(); } }
當調用者須要一個具體產品對象,只要使用該具體產品的具體工廠建立該對象便可。調用者不須要知道具體產品對象是怎麼建立的
便於擴展:使用工廠方法模式的另外一個優勢是在系統中加入新產品時,無須修改抽象工廠和抽象產品提供的接口,無須修改客戶端,也無須修改其餘的具體工廠和具體產品,而只要添加一個具體工廠和具體產品就能夠了。這樣,系統的可擴展性也就變得很是好,徹底符合「開閉原則」。
工廠方法模式是平行的類層次結構
什麼是平行的類層次結構?簡單的說,假若有兩個類的層次,其中一個層次中的類在另一個層次中都有相對應的類,則稱這兩個類層次是平行的類層次結構。工廠方法模式就是平行的類層次結構。這裏能夠看UML圖,很是明顯!工廠類層次和產品類層次就是平行的。
這種平行的類層次結構用來幹什麼呢?主要用來把一個類層次中的某些行爲分離出來,讓類層次中的類把本來屬於本身的職責,委託給分離出來的類去實現,從而使得類層次自己變得更簡單,更容易擴展和複用。
工廠方法模式是new一個對象的替代品。因此其實在須要大量實例化對象的地方都是可使用的。
實際中開發中,工廠方法主要用於工具包和框架中
爲何工廠方法模式也稱爲多態工廠模式?工廠接口的有不一樣的實現(也就是多態),換一句說也就是具體工廠類都有同一個工廠接口。
工廠方法模式的簡化:如果產品對象較少,可使用一個具體工廠類(包含一個靜態的工廠方法)根據參數去建立全部的具體產品,這就是所謂的簡單工廠模式。
注意簡單工廠模式就是經過參數化工廠方法實現的。參數化工廠方法具體指:經過給工廠方法傳遞參數,讓工廠方法根據參數建立不一樣的產品對象。
詳細可參考《研磨設計模式》