泛型不僅能用來做容器,還能夠提供代碼復用的手段。在泛型的參與下,許多設計就可能更精妙,更具擴展性。今天我就來演示一個利用泛型增強的抽象工廠模式。我們知道,抽象工廠(abstract factory)模式是將工廠和產品全部抽象化,一個抽象工廠生成一組抽象產品,而一個具體工廠則生成具體產品的一個特定組合。它能夠維持這種相關對象組合的一致性,并使得用戶不需要了解工廠和產品的具體實現。傳統的abstract factory主要弱點是類型依賴性強,可復用性弱。一個抽象工廠通常是為一個特定需要而設計,通常不能被其他需要抽象工廠的場合使用。而且盡管抽象工廠可以將實際生產任務委派給特定類型的工廠,但這種委派是需要通過自己純代碼實現的,沒能利用語言所提供的抽象特性。我們今天的任務就是編寫一個不針對特定產品類型和數目的泛型抽象工廠,當你需要特定的抽象工廠時,可隨時復用無需再定義專門的抽象工廠實現。
我們首先從只生成一個產品的工廠方法開始,以這作為抽象工廠的原料。很明顯,可以設計這樣一個接口作為工廠方法的模板:
public interface ifactory<t>
{
t create();
}
這個工廠生產一個t類型的對象。當你實現此工廠時,應該讓t為抽象產品的類型——即產品通用的基類。比如我們可以實現一個采用無參數構造函數來創建對象的opnewfactory實現:
public class opnewfactory<tabstractproduct, tproduct> : ifactory<tabstractproduct>
where tproduct : tabstractproduct, new()
{
public tabstractproduct create()
{
return new tproduct();
}
}
從此例子可以看出,你應該僅實現抽象類型的ifactory接口,并生成具體類型。現在我們做完了單一產品的工廠方法模板,就要開始定義生產多個產品的抽象工廠接口了。.net泛型支持按類型參數個數進行重載,就是說,我們可以定義生產一個、兩個、三個……等多種數目的抽象工廠接口,而使用同一個名字。(汗吧,這就是所謂支持“任意數目”的手法)這里免不了要拷貝代碼,不過別擔心,純拷貝而已,使用的時候可是非常舒心的哦。我們以生產兩種產品類型的抽象工廠為例介紹。能不能定義成這樣呢?
public interface iabstractfactory<t1, t2>
{
t1 create();
t2 create(); //編譯錯誤!!!
}
哦不!方法不能以返回類型區分重載,只能靠參數類型重載。然而這里我們顯然不能用t1和t2作為參數,因為這些方法就是為了生產t1和t2準備的,怎么能接受它們作為參數呢?難道要命名為create1和create2嗎?這很難接受,我們希望生產方法能夠體現產品的類型,怎么能叫1和2呢。為了解決這個問題,我們引入了typetoken<t>類型,它的定義如下:
public sealed class typetoken<t>
{
static private typetoken<t> instancevalue = new typetoken<t>();
static public typetoken<t> instance
{
get { return instancevalue; }
}
private typetoken() { }
}
這個類沒有成員,并且每個類型實參只能創建一個實例,因此代價極小。但就是這小小的實例上帶有其類型實參的類型信息,因此可以作為判斷函數重載的依據。我們用typetoken<t>作為區分生產函數重載的依據,實現如下:
public interface iabstractfactory<t1, t2>
{
t1 create(typetoken<t1> token);
t2 create(typetoken<t2> token);
}
現在我們針對抽象工廠實現具體工廠。具體工廠就是利用生產每種產品的單一工廠來組合實現。因此你只要有每種類型的單一工廠就可以直接組合生成抽象工廠,而無需定義一個類來做這件事。注意,對每種數目的抽象工廠接口都需要對應生成一個具體工廠的實現,這里我僅針對生成兩個產品的演示:
public class concretefactory<t1, t2> : iabstractfactory<t1, t2>
{
private ifactory<t1> factory1;
private ifactory<t2> factory2;
public concretefactory(ifactory<t1> f1, ifactory<t2> f2)
{
factory1 = f1;
factory2 = f2;
}
public t1 create(typetoken<t1> token)
{
return factory1.create();
}
public t2 create(typetoken<t2> token)
{
return factory2.create();
}
}
public static class concretfactory
{
public static concretefactory<t1, t2> newfactory<t1, t2>(ifactory<t1> f1, ifactory<t2> f2)
{
return new concretefactory<t1, t2>(f1, f2);
}
}
注意,我又聲明了一個沒有類型參數的concretfactory類,用一個靜態方法來生成泛型concretfactory的實例,這是因為使用泛型方法可以推測類型參數,使得我們可以不必輸入尖括號或of語句,而泛型類則沒有這個功能。現在大功告成!我們用一個例子來演示這個泛型抽象工廠的工作情況。現在假設我們需要一個生產pc的抽象工廠,需要生產兩種抽象產品:處理器和內存。處理器和內存的抽象和具體實現如下:
processor 和 ram
public abstract class processor
{
public abstract string model { get; }
}
public abstract class ram
{
public abstract int frequency { get;}
}
public class pentiumprocessor : processor
{
public override string model
{
get { return "pentium extreme edition 955"; }
}
}
public class athlonprocessor : processor
{
public override string model
{
get { return "athlon 64 x2 fx-60"; }
}
}
public class ddrram : ram
{
public override int frequency
{
get { return 400; }
}
}
public class ddr2ram : ram
{
public override int frequency
{
get { return 533; }
}
}
下面的代碼演示了如何隨心所欲生成想要的抽象工廠接口以及快速從現有單一產品工廠組合成特定的具體工廠實現。
class program
{
static iabstractfactory<processor, ram> computerfactory(string type)
{
if (type == "intel")
{
return concretfactory.newfactory( new opnewfactory<processor, pentiumprocessor>(),
new opnewfactory<ram, ddr2ram>());
}
else if (type == "amd")
{
return concretfactory.newfactory( new opnewfactory<processor, athlonprocessor>(),
new opnewfactory<ram, ddrram>());
}
//unknown type
return null;
}
static void main(string[] args)
{
//yield a computer of intel
iabstractfactory<processor, ram> factory1 = computerfactory("intel");
ram ram1 = factory1.create(typetoken<ram>.instance);
processor cup1 = factory1.create(typetoken<processor>.instance);
console.writeline("an intel computer");
console.writeline("cpu model: {0}", cup1.model);
console.writeline("memory frequency: {0} mhz", ram1.frequency);
console.writeline();
//yield a computer of amd
iabstractfactory<processor, ram> factory2 = computerfactory("amd");
ram ram2 = factory2.create(typetoken<ram>.instance);
processor cup2 = factory2.create(typetoken<processor>.instance);
console.writeline("an amd computer");
console.writeline("cpu model: {0}", cup2.model);
console.writeline("memory frequency: {0} mhz", ram2.frequency);
}
}
總結:
我們用泛型技術成功地增強了原本重用性較低的抽象工廠,演示了泛型在提高抽象性和代碼重用方面卓越的價值。菜鳥學堂: