意圖
定義一個創建產品對象的工廠接口,將實際創建工作推遲到子類中。
場景
上次,我們使用抽象工廠解決了生產一組產品的問題,但是我們把各個場景作為了具體工廠來生產場景模式和場景紋理兩個產品。在調用代碼中也並沒有出現具體工廠的影子。其實,場景類要做的不僅僅是創建具體的產品系列,可能它還需要做一個初始化工作。那麼,我們就需要在調用代碼中能得到這個場景類。
在前一節中,由於場景類(比如HalfPaper)本身是具體級別的(具體工廠)。那麼,我們也不應該在調用代碼中直接依賴場景類。因此,我們可以使用工廠方法來生產這個具體產品。
示例代碼
using System;
using System.Reflection;
namespace FactoryMethodExample
{
class Program
{
static void Main(string[] args)
{
Patrix patrix = new Patrix();
patrix.LoadScene("HalfPaper");
patrix.LoadScene("Matrix");
}
}
class Patrix
{
private PatrixSceneFactory GetGameScene(string gameSceneName)
{
return (PatrixSceneFactory)Assembly.Load("FactoryMethodExample").CreateInstance("FactoryMethodExample." + gameSceneName + "Factory");
}
public void LoadScene(string gameSceneName)
{
PatrixSceneFactory psf = GetGameScene(gameSceneName);
PatrixScene ps = psf.CreateScene();
ps.InitScene();
}
}
abstract class PatrixSceneFactory
{
public abstract PatrixScene CreateScene();
}
abstract class PatrixScene
{
public void InitScene()
{
Texture texture = CreateTexture();
Model model = CreateModel ();
model .FillTexture(texture);
}
public abstract Model CreateModel ();
public abstract Texture CreateTexture();
}
abstract class Model
{
public abstract void FillTexture(Texture texture);
}
abstract class Texture
{
}
class HalfPaperFactory : PatrixSceneFactory
{
public override PatrixScene CreateScene()
{
return new HalfPaper();
}
}
class HalfPaper : PatrixScene
{
public HalfPaper()
{
Console.WriteLine("HalfPaper Creating");
}
public override Model CreateModel ()
{
return new HalfPaperModel ();
}
public override Texture CreateTexture()
{
return new HalfPaperTexture();
}
}
class HalfPaperModel : Model
{
public HalfPaperModel ()
{
Console.WriteLine("HalfPaper Model Creating");
}
public override void FillTexture(Texture texture)
{
Console.WriteLine("HalfPaper Model is filled Texture");
}
}
class HalfPaperTexture : Texture
{
public HalfPaperTexture()
{
Console.WriteLine("HalfPaper Texture Created");
}
}
class MatrixFactory : PatrixSceneFactory
{
public override PatrixScene CreateScene()
{
return new Matrix();
}
}
class Matrix : PatrixScene
{
public Matrix()
{
Console.WriteLine("Matrix Created");
}
public override Model CreateModel ()
{
return new MatrixModel ();
}
public override Texture CreateTexture()
{
return new MatrixTexture();
}
}
class MatrixModel : Model
{
public MatrixModel ()
{
Console.WriteLine("Matrix Model Created");
}
public override void FillTexture(Texture texture)
{
Console.WriteLine("Matrix Model is filled Texture");
}
}
class MatrixTexture : Texture
{
public MatrixTexture()
{
Console.WriteLine("Matrix Texture Created");
}
}
}
代碼執行結果如下圖:
代碼說明
l 這個代碼基於前一節抽象工廠的代碼修改而來,因此代碼比較廠。其中能體現的設計模式有抽象工廠、工廠方法以及模版方法。
l 這次的PatrixSceneFactory和前一節的不同,它是一個產品的抽象工廠,也就是工廠方法模式中的抽象工廠角色,它是具體產品工廠的抽象形式。
l HalfPaperFactory和MatrixFactory是工廠方法模式中的具體工廠角色,它負責創建具體的產品。
l PatrixScene是工廠方法模式中的抽象產品角色,同時也是抽象工廠模式中的抽象工廠角色。它既是場景的抽象形式,又負責創建每個場景中的產品系列,也就是模型和紋理。還有,它的InistScene()方法還體現了模版方法的思想,封裝了產品初始化過程中的共同步驟。
l HalfPaper和Matrix當然就是工廠方法模式中的具體產品角色了,同時,它們也是抽象工廠模式中的具體工廠角色。
l 從這個例子可以看出,抽象工廠針對一組產品的創建進行抽象,抽象程度比較高。抽象工廠生產重點在於規范一組產品的創建,能讓產品線保持產品的一致。比如,N卡不管是7系列還是8系列,總會分低端的7300,8300和中端的7600,8600以及高端的7900,8900。
l 而工廠方法針對某種產品的創建,每種產品在創建的過程中可能會有一些相似的步驟,那麼就可以在抽象產品中進行一些提取,自然而然運用到了模版方法。工廠方法還能針對具體產品創建時的易變性,在這裡我們可能很清楚HalfPaperFactory一定會創建HalfPaper這個產品,但是萬一以後改為創建HalfPaperSpecial了呢?有了工廠方法,我們可以只需修改HalfPaperFactory就可以了。
l 不管怎麼樣,目的還是讓調用方盡量和接口依賴(或者說和穩定的東西去依賴,讓變化在接口下面變),既是要以來具體類型,也希望能只依賴一個。試想一下,如果沒有抽象工廠和工廠方法,也少了這些抽象類型,那麼調用方可能就要依賴具體場景類型和具體的紋理以及模型類型。並且在調用的時候,通過條件來判斷並且創建各種具體類型,一旦有新的場景需要實現,調用方代碼可能就需要做很大的調整。暫且不說調整的工作量有多少,調整所帶來的風險誰能承擔呢?
何時采用
l 從代碼角度來說, 如果我們需要創建一個易變的對象,或是希望對象由子類決定創建哪個對象的時候可以考慮工廠方法。
l 從應用角度來說, 如果我們覺得具體產品的創建不穩定,或者客戶端根本無需知道創建哪個具體產品的時候可以使用工廠方法。後者對於框架和工具包軟件來說更常見,比如有一個打印類負責打印圖紙,我們需要得到一個打印對象,對於調用方來說並不知道要使用超寬打印對象還是普通打印對象,我們可以通過工廠方法使客戶端和抽象打印工廠直接溝通,由它來決定具體創建哪個打印對象。
實現要點
l 通過繼承創建具體產品。很多時候,每一種具體產品對應一個具體的工廠來創建。
l 使用具體工廠類來決定怎麼樣創建具體產品。調用方並不關心工廠創建的是哪個游戲場景,它只用知道工廠給我的是一個游戲場景即可。
注意事項
l 工廠方法通常需要為每個具體產品對應一個具體工廠,如果濫用的話會使得類的數目急劇增多。