意圖
提供一個創建一系列相關或相互依賴對象的接口,而無需指定它們具體的類。
場景
還是上次說的那個網絡游戲,定下來是一個休閒的FPS游戲。和CS差不多,8到16個玩家在游戲裡面分成2組對戰射擊。現在要實現初始化場景的工作。要呈現一個三維物體一般兩個元素是少不了的,一是這個物體的骨架,也就是模型,二就是這個骨架上填充的紋理。
我們知道,這樣的一個游戲不可能只有一張地圖,而且地圖的數量肯定是會一直增加的。如果游戲在初始化場景的時候需要根據不同的地圖分別加載模型和紋理對象,那麼勢必就會使得場景的擴充變得很不方便。由此,我們引入Abstract Factory,抽象工廠生產的都是實際類型的接口(或者抽象類型),如果加了新的場景可以確保不需要修改加載場景的那部分代碼。
示例代碼
using System;
using System.Reflection;
namespace AbstractFactoryExample
{
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("AbstractFactoryExample").CreateInstance("AbstractFactoryExample." + gameSceneName);
}
public void LoadScene(string gameSceneName)
{
PatrixSceneFactory psf = GetGameScene(gameSceneName);
Texture texture = psf.CreateTexture();
Model model = psf.CreateModel ();
model .FillTexture(texture);
}
}
abstract class PatrixSceneFactory
{
public abstract Model CreateModel ();
public abstract Texture CreateTexture();
}
abstract class Model
{
public abstract void FillTexture(Texture texture);
}
abstract class Texture
{
}
class HalfPaper : PatrixSceneFactory
{
public override Model CreateModel ()
{
return new HalfPaperModel ();
}
public override Texture CreateTexture()
{
return new HalfPaperTexture();
}
}
class HalfPaperModel : Model
{
public HalfPaperModel ()
{
Console.WriteLine("HalfPaper Model Created");
}
public override void FillTexture(Texture texture)
{
Console.WriteLine("HalfPaper Model is filled Texture");
}
}
class HalfPaperTexture : Texture
{
public HalfPaperTexture()
{
Console.WriteLine("HalfPaper Texture Created");
}
}
class Matrix : PatrixSceneFactory
{
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 PatrixSceneFactory就是一個抽象工廠,它聲明了創建抽象的場景以及抽象的紋理的接口。(廣告時間:Patrix是我公司的一款休閒FPS游戲,詳細請見http://www.qwd1.com)
l Model和Texture是抽象產品。在Model類中有一個抽象方法,用於為模型填充紋理。
l HalfPaper和Matrix是具體工廠,它用於創建某個場景的模型和紋理。(你可能對兩個類的名字不太理解,其實HalfPaper和Matrix是兩個地圖的名字)
l xxxModel和xxxTexture就是具體的產品了。它們就是針對某個場景的模型和紋理,具體工廠負責創建它們。
l Patrix這個類負責加載場景,為了避免加載不同場景使用case語句,在這裡我們使用反射來加載具體工廠類。
l 可以看到,一旦有了新的場景(或者說地圖),我們只需要設計新的xxxModel和xxxTexture以及具體工廠類就可以了,加載場景的那部分代碼(也就是Patrix類)不需要做改動。
l 我們現在這個游戲可是不支持和電腦對戰的,萬一以後需要支持電腦了,那麼場景中的元素除了紋理和模型之外就還需要加電腦了。也就是說抽象工廠還需要多生產一種類型的產品,這個時候抽象工廠就無能為力了。抽象工廠只能解決系列產品擴張的變化點(在我們的例子中就是地圖的新增),因此千萬把抽象工廠所能生產的產品考慮周全了。
何時采用
l 從代碼角度來說,你希望在統一的地方創建一系列相互關聯的對象,並且基於抽象對象的時候。
l 從應用角度來說,如果你的產品是成組成套的,並且肯定會不斷擴展新系列的,那麼就適用抽象工廠。比如說,外面買的塑料模型,裡面總是有圖紙、模型元件板和外殼包裝三部分。那麼生產模型的廠就是抽象工廠,打印圖紙、打印包裝盒以及生產元件板的三個流水線就是具體工廠了。需要生產新的模型,只需要制作新的圖紙輸入到三個流水線的電腦中就可以了。
實現要點
l 抽象工廠本身不負責創建產品,產品最終還是由具體工廠來創建的。比如,MatrixModel是Matrix創建的,而不是PatrixSceneFactory創建的。在.NET中可以使用反射來創建具體工廠,從而使得代碼變動降到最低。
l 在抽象工廠中需要體現出生產一系列產品。這一系列產品是相互關聯,相互依賴一起使用的。
l 抽象工廠對應抽象產品,具體工廠對應具體產品,外部依賴抽象類型,這樣對於新系列產品的創建,外部唯一依賴的就是具體工廠的創建過程(可以通過反射解決)。
注意事項
l 一般來說需要創建一系列對象的時候才考慮抽象工廠。比如,創建一個場景,需要創建模型和紋理,並且模型和紋理之間是有一定聯系的,不太可能把PatrixTexture套用在MatrixModel上。
l 如果系統的變化點不在新系列的擴充上,那麼就沒有必要使用抽象工廠。比如,如果我們不會增加新地圖的話,那麼也就沒有必要引入抽象工廠。
.NET中的抽象工廠
l 我們說過,抽象工廠針對系列產品的應變。在使用ADO.NET進行數據訪問的時候,如果目標數據庫是Access,我們會使用OleDbConnection、OleDbCommand以及OleDbDataAdapter等一系列ADO.NET對象。那麼如果數據庫是SQl Server,我們又會改用SqlConnection、SqlCommand以及SqlDataAdapter等一系列ADO.NET對象。如果只使用一套對象,沒有什麼大問題,如果我們的數據訪問有系列變化的需求,比如可以針對Access和SQl Server,而且希望改換數據庫盡量對客戶端代碼透明,那麼就需要引入抽象工廠模式。
l 好在,ADO.NET 2.0中已經有了整套抽象工廠的類型。看下面的代碼,你應該能辨別這些類型在抽象工廠中的角色:
public abstract class DbProviderFactory
{
// Methods
protected DbProviderFactory()
{
}
public virtual DbCommand CreateCommand()
{
return null ;
}
public virtual DbCommandBuilder CreateCommandBuilder()
{
return null ;
}
public virtual DbConnection CreateConnection()
{
return null ;
}
public virtual DbConnectionStringBuilder CreateConnectionStringBuilder()
{
return null ;
}
public virtual DbDataAdapter CreateDataAdapter()
{
return null ;
}
public virtual DbDataSourceEnumerator CreateDataSourceEnumerator()
{
return null ;
}
public virtual DbParameter CreateParameter()
{
return null ;
}
public virtual CodeAccessPermission CreatePermission(PermissionState state)
{
return null ;
}
// Properties
public virtual bool CanCreateDataSourceEnumerator
{
get
{
return false;
}
}
}
public sealed class OleDbFactory : DbProviderFactory
{
// Fields
public static readonly OleDbFactory Instance = new OleDbFactory();
// Methods
private OleDbFactory()
{
}
public override DbCommand CreateCommand()
{
return new OleDbCommand();
}
public override DbCommandBuilder CreateCommandBuilder()
{
return new OleDbCommandBuilder();
}
public override DbConnection CreateConnection()
{
return new OleDbConnection();
}
public override DbConnectionStringBuilder CreateConnectionStringBuilder()
{
return new OleDbConnectionStringBuilder();
}
public override DbDataAdapter CreateDataAdapter()
{
return new OleDbDataAdapter();
}
public override DbParameter CreateParameter()
{
return new OleDbParameter();
}
public override CodeAccessPermission CreatePermission(PermissionState state)
{
return new OleDbPermission(state);
}
}
public sealed class SqlClientFactory : DbProviderFactory, IServiceProvider
{
// Fields
public static readonly SqlClientFactory Instance = new SqlClientFactory();
// Methods
private SqlClientFactory()
{
}
public override DbCommand CreateCommand()
{
return new SqlCommand();
}
public override DbCommandBuilder CreateCommandBuilder()
{
return new SqlCommandBuilder();
}
public override DbConnection CreateConnection()
{
return new SqlConnection();
}
public override DbConnectionStringBuilder CreateConnectionStringBuilder()
{
return new SqlConnectionStringBuilder();
}
public override DbDataAdapter CreateDataAdapter()
{
return new SqlDataAdapter();
}
public override DbDataSourceEnumerator CreateDataSourceEnumerator()
{
return SqlDataSourceEnumerator.Instance;
}
public override DbParameter CreateParameter()
{
return new SqlParameter();
}
public override CodeAccessPermission CreatePermission(PermissionState state)
{
return new SqlClientPermission(state);
}
object IServiceProvider.GetService(Type serviceType)
{
object obj2 = null ;
if (serviceType == GreenMethods.SystemDataCommonDbProviderServices_Type)
{
obj2 = GreenMethods.SystemDataSqlClientSqlProviderServices_Instance();
}
return obj2;
}
// Properties
public override bool CanCreateDataSourceEnumerator
{
get
{
return true;
}
}
}