回顧下設計模式系列《工廠模式》那片文章,里面描述了如何利用工廠模式來模擬一個換燈泡的場景,該場景中模擬了:普通燈泡、節能燈泡、彩色燈泡。它們統一由一個工廠(工廠類)來創造,我們需要使用哪種燈泡時,只需通知工廠類給我們打造一個相同的燈泡即可,類圖如下:

由上面邊的類圖可見,所有類型的燈泡都由燈泡工廠來創建,那這個時候,制造燈泡的工廠因為企業擴大了,需要增加產量,那么此時一個工廠肯定是應付不過來了,而且當前一個工廠所造的燈泡種類也多,更加加大了工廠的制造壓力,此時,企業擴建的最好辦法就是,增加工廠,各自工廠都只專注於一種燈泡的制造。
1.工廠方法模式
首先我們來看看什么是工廠方法模式:定義一個用於創建對象的接口,讓子類決定實例化哪一個類。工廠方法使一個類的實例化延遲到其子類。
工廠方法模式類圖如下:

Product:定義工廠方法所創建的對象的接口。相當於上邊場景中的燈泡抽象。
ConcreteProduct:具體實現Product抽象的具體產品類。相當於上邊場景中的具體燈泡。
Creator:聲明工廠方法,該方法返回一個Product類型的對象。
ConcreteCreator:重定義工廠方法返回一個ConcreteProduct實例。
可能這會大家看完這個類圖會有點模糊,不過沒關系,接下來我們利用工廠方法模式來實現我們上邊的場景(企業擴大需要建立更多的工廠)。
首先,企業擴大了,需要增加產量建立更多的工廠來創建,為了更好的提高生產效率,每個工廠只專注於生產一種種類的燈泡,利用工廠方法模式設計的類圖如下:

邊分析邊實現代碼:
第一步,工廠擴大了,所造的產品還是燈泡,所以我們還是要把燈泡先抽象出來,為何要抽象請參考《工廠模式》這篇文章。
public interface IBulb
{
// 統一的發光接口
public void Luminescence();
}
第二步,還是實現目前所有種類的具體燈泡類型。代碼如下:
public class Bulb : IBulb // 實現了燈泡的約定、標准 {
// 發光
public void Luminescence()
{
// 燈泡發光的實現
}
}
// 節能燈泡
public class FrugalBulb : IBulb // 實現了燈泡的約定、標准
{
// 節能發光
public void Luminescence()
{
// 節能燈泡發光的實現
}
}
// 彩色燈泡
public class ColorBulb : IBulb
{
// 彩色發光
public void Luminescence()
{
// 彩色燈泡發光的實現
}
}
第三步,因為增加了工廠,但是每個工廠都有一個共同的行為那就是制造燈泡,因此為了將來更方便的增加工廠,我們把工廠也抽象,代碼如下:
public interface IMyBulbFactory
{
IBulb GetBulb(); // 將來每個工廠都有制造燈泡的行為
}
第四步,實現制造每個種類燈泡的具體工廠,每個工廠都只制造自己所造種類的燈泡,代碼如下:
public class BulbFactory : IMyBulbFactory
{
// 該工廠制造燈泡的行為 只能制造普通燈泡
public IBulb GetBulb()
{
return new Bulb();
}
}
// 制造節能燈泡的工廠
public class FrugalBulbFactroy : IMyBulbFactory
{
// 該工廠制造燈泡的行為 只能制造節能燈泡
public IBulb GetBulb()
{
return new FrugalBulb();
}
}
// 制造節能燈泡的工廠
public class ColorBulbFactroy : IMyBulbFactory
{
// 該工廠制造燈泡的行為 只能制造彩色燈泡
public IBulb GetBulb()
{
return new ColorBulb();
}
}
這樣,就滿足了企業擴大了,產量提高的問題了,需要什么樣的燈泡我們只需要到具體的工廠去獲取對應的燈泡就可以啦,主函數調用如下:
{
// 需要普通燈泡
IMyBulbFactory bulbFactory = new BulbFactory();
IBulb bulb = bulbFactory.GetBulb();
bulb.Luminescence(); // 普通燈泡發光
// 需要節能燈泡
IMyBulbFactory frugalBulbFactroy = new FrugalBulbFactroy();
IBulb frugalBulb = frugalBulbFactroy.GetBulb();
frugalBulb.Luminescence(); // 節能燈泡發光
// 需要彩色燈泡
IMyBulbFactory colorbulbFacroty = new ColorBulbFactroy();
IBulb colorBulb = colorbulbFacroty.GetBulb();
colorBulb.Luminescence(); // 彩色燈泡發光
}
那么我們發現,當工廠方法把具體產品的實例化延遲到具體的工廠類時,在客戶端調用的時候,我們必須顯示的制定使用哪個具體工廠類,這樣又加大了耦合度。
2.使用反射的工廠方法模式
那么如何接觸上邊的耦合度呢?可以通過C#的反射機制,將當前所有工廠都配置到配置文件中,然后在配置一個當前需要使用的工廠節點,配置文件如下:
< configuration >
< appSettings >
<!-- 配置所有現有的工廠 -->
< add key ="bulbFactory" value ="設計模式系列.工廠方法模式.BulbFactory" ></ add >
< add key ="frugalBulbFactory" value ="設計模式系列.工廠方法模式.FrugalBulbFactroy" ></ add >
< add key ="ColorBulbFactory" value ="設計模式系列.工廠方法模式.ColorBulbFactroy" ></ add >
<!-- 配置當前使用的工廠 當前使用普通燈泡的工廠 value="bulbFactory" -->
< add key ="NonFactory" value ="bulbFactory" ></ add >
</ appSettings >
</ configuration >
這是我們還需要封裝一個專門提供具體工廠的類,為了方便這個類我就不考慮耦合了度。代碼如下:
public class FactroyProvider
{
public static IMyBulbFactory WhereToFactroy()
{
string fName = string.Empty;
string factoryName = string.Empty;
IMyBulbFactory factory = null;
if(! string.IsNullOrEmpty(ConfigurationManager.AppSettings[ " NonFactory "]))
{
factoryName = ConfigurationManager.AppSettings[ " NonFactory "];
if (! string.IsNullOrEmpty(ConfigurationManager.AppSettings[factoryName]))
{
fName = ConfigurationManager.AppSettings[factoryName];
factory = Assembly.Load( " 設計模式系列 ").CreateInstance(fName) as IMyBulbFactory;
}
}
return factory;
}
}
通過反射找到工廠,我們將來如果又要擴大公司規模只需要增加具體的工廠實例與產品實例,在配置文件中配置一下即可。
3.工廠方法模式與工廠模式的區別
說道這里,不知道大家會不會有一個疑問,總之剛開始我是有這個疑問,疑問就是它和工廠模式的區別是什么呢?
首先我們回顧下《工廠模式》中的工廠類的代碼,代碼如下:
{
public static IBulb GetBulb( string bulbName)
{
IBulb bulb = null;
// 告訴我你要什么燈泡,我制造相應的燈泡給你
switch (bulbName)
{
case " bulb ":
bulb = new Bulb();
break;
case " frugalbulb ":
bulb = new FrugalBulb();
break;
case " colorbulb ":
bulb = new ColorBulb();
break;
}
return bulb;
}
}
可見將來我們如果需要新增一種燈泡,需要修改上面的工廠類,在switch中加入一個對應的case分支,這樣就違背了開放封閉原則,而工廠方法就是將這些產品類型的實例化延遲到具體的工廠類去實例化,這樣將來如果有了新產品就只需要去創建一個新的工廠類和一個具體的產品就可以了,也就是說工廠方法模式完善了簡單工廠模式,使它滿足了開放封閉原則。但是有個問題,就是客戶端每次調用的時候都會需要制定具體實例化哪個工廠類,例如:
IMyBulbFactory bulbFactory = new BulbFactory(); // 這里就制定了具體的工廠類
IBulb bulb = bulbFactory.GetBulb();
bulb.Luminescence(); // 普通燈泡發光
這樣就增加了耦合性。我們在上面使用了反射類解除了耦合,但是又一個疑問出來啦,那么簡單工廠模式在創建產品類的時候,如果也使用反射的話那么將來在新增產品的時候也就不需要修改工廠類的代碼了,也就是說簡單工廠模式使用反射一樣可以完美的解決開放封閉原則的問題!
那么新的問題又出來了,那簡單工廠模式還有什么作用呢?
我個人有兩種理解的方式,僅代表個人觀點,也希望大家都詳細的說說自己的觀點,不要一句話概括噢~!
第一種、在早期設計模式應用的時候,編程語言還沒有使用反射的機制時,使用工廠方法模式來升級簡單工廠模式,使能夠支持開放封閉原則。
第二種、在特殊場景中,工廠方法模式能更好的實現邏輯的組織,例如本篇文章所使用的場景。
