ITK 中使用的其實是種稱爲 pluggable object factory 的設計模式,它可以在運行時進行動態替換,而且可以不修改已有代碼的情況下添加處理新格式的圖像文件類,等還有一些其它優點。它由兩種模式組合而成:1.Abstract Factory 抽象工廠;2. Prototype 原型模式。還可能與單件模式(Singleton)組合。
簡單的對象工廠可能如下所示:
1:
2: //Simple Factory
3: Circle
4: {
5: double radius;
6: }
7:
8: class Shape
9: {
10: public:
11: static Shape* newShape(istream&);
12: };
13: Shape* Shape::newShape(istream& params)
14: {
15: string className;
16: params >> className;
17: if (className == "Circle")
18: return new Circle(params);
19: else if (className == "Triangle")
20: return new Triangle(params);
21: else
22: //…
23: }
24:
Shape 是一個用來創建不同形狀的對象工廠,Circle 是一種具體的形狀。對象工廠 Shape 通過判斷傳入的參數,創建不同的對象實例。簡單對象工廠的問題在於:1.基類 Shape 必須知道從它繼承的每一個具體子類。2.當需要添加一種新的形狀時,就得修改工廠方法 newShape()。這就違背了面向對象設計的“依賴倒置”原則(Dependency Inversion Principle),即高層模塊不應該依賴於低層模塊。同時還違背了“開-閉”原則(Opened-Closed),即 Open to extension,Closed to Modification。Pluggable Object Factory 模式可以解決這些問題。
Pluggable Object Factory,或者稱爲 PROTOTYPE–ABSTRACT FACTORY,可以將該模式歸爲創建型一類。
使用一個關聯式容器如 map,關聯容器中的每一個元素都是一個對象工廠,該對象工廠知道如何從一個抽象類層次結構中創建具體的子類對象實例。關聯容器中的 key 則是用來從容器中查找合適的對象工廠。
Prototype Factory Compound Pattern:使用工廠方法,我們創建一個抽象類 ShapeMaker,它有一個靜態的公有方法 newShape。一個具體的 CircleMaker 知道如何創建 circle 對象,它繼承自 ShapeMaker。
1: //
2: class ShapeMaker
3: {
4: public:
5: static Shape* newShape(istream&);
6: protected:
7: typedef map<string,ShapeMaker*> MakerMap;
8: virtual Shape* makeShape(istream&) const=0;
9: static MakerMap registry;
10: };
11: Shape* ShapeMaker::newShape(istream& is) {
12: string className;
13: is >> className;
14: ShapeMaker* maker =
15: (*registry.find(className)).second;
16: return maker->makeShape(is);
17: }
18: //
19: class CircleMaker : public ShapeMaker
20: {
21: private:
22: Shape* makeShape(istream& params) const
23: {
24: return new Circle(params);
25: }
26: };
27:
1:
2: //
3: class CircleMaker : public ShapeMaker
4: {
5: private:
6: CircleMaker() : ShapeMaker("Circle") {}
7: static const CircleMaker registerThis;
8: };
9: //其中:
10: ShapeMaker::ShapeMaker(string className)
11: {
12: //在基類的靜態關聯容器中註冊自己
13: registry.insert( make_pair(className, this) );
14: }
15: //使用:
16: fstream params("shapes.txt");
17: Shape* shape = ShapeMaker::newShape(params);
18:
Plugging in with Prototype Factory:Factory Method 工廠方法與 Prototype 原型模式結合,允許工程擴展 ShapeMaker可以創建對象的 Shape 類型。ShapeMaker 類以及與其協作的其它類表示了 Prototype Factory 模式。Factory Method 工廠方法提供創建對象實例的方法,Prototype 原型模式提供在運行時動態註冊對象工廠的方法。
1.GENERIC FACTORIES 泛化工廠:從上面的 Prototype Factory 模式可以看出,算法與數據類型是相互獨立的,所以我們使用模板實現 Prototype Factory 組合模式:
1:
2: //Generic 工廠
3: template<class Object>
4: class Maker
5: {
6: public:
7: virtual ~Maker();
8: static Object* newObject(istream&);
9: protected:
10: Maker(const string& className);
11: virtual Object* makeObject(istream&) const=0;
12: private:
13: typedef Maker
14: typedef map<string,MakerPtr> MakerMap;
15: static MakerMap registry;
16: };
17:
這樣,ShapeMaker 便可如下重寫:
1: //
2: template<class Shape>
3: class ShapeMaker : public Maker
4: {
5: protected:
6: ShapeMaker(const string& className)
7: : Maker(className) {}
8: };
9: class CircleMaker : public ShapeMaker
10: {
11: private:
12: CircleMaker () : ShapeMaker ("Circle") {}
13: Shape* makeObject(istream& params) const {
14: return new Circle(params); }
15: static const CircleMaker registerThis;
16: };
17:
還可以很容易地創建其它對象工廠的抽象類層次,如下:
1: //
2: template<class CoordSys>
3: class CoordSysMaker : public Maker
4: {
5: //…
6: };
7: class XyzMaker : public CoordSysMaker
8: {
9: //…
10: };
11: class RasterMaker : public CoordSysMaker
12: {
13: //…
14: };
15:
Making Objects from Files:有時我們可能不使用 C++ 的 istream 流作爲 newObject() 的參數,而想使用 FILE*,我們可以將 Maker 的模板參數如下修改即可:
1: //
2: template <class Object, class Params>
3: class Maker
4: {
5: public:
6: static Object* newObject(Params);
7: protected:
8: typedef Maker
9: virtual Object* makeObject(Params) const=0;
10: };
11: //
12: class ShapeMaker
13: : public Maker
14: {
15: //...
16: };
17: class LegacyShapeMaker
18: : public Maker
19: {
20: //…
21: };
22:
Making Objects from Aggregate Classes:我們還可以從一個包含某個具體子類數據成員的聚集類構造對象:
1: //
2: class GeomModel
3: {
4: //...
5: private:
6: const char* sensorName;
7: FILE* geomFile;
8: const char* outputDir;
9: };
10: //結構體,用於傳遞構造 GeomModel 所需的參數
11: struct GeomModelParams
12: {
13: const char* sensorName;
14: FILE* geomFile;
15: const char* outputDir;
16: };
17: class GeomModelMaker
18: : public Maker
19: {
20: //…
21: };
22:
2.Chain of Factories:如果我們要創建的對象的類型不是流的第一個元素,那麼上面的工廠就會失效。我們可以通過將職責鏈模式(Chain of Responsibility)組合到我們 Pluggable 工廠模式來解決這個問題。職責鏈模式允許我們順序迭代一個 Makers 鏈表,讓 makers 決定哪一個 maker 可以創建該對象。在職責鏈上通用的查找機制可能如下:
1: //
2: Object* Maker
3: ::newObject(Params params)
4: {
5: Object* object = 0;
6: for (const_iterator iter = registry.begin();
7: !object && iter != registry.end(); ++iter )
8: {
9: MakerPtr maker = (*iter).second;
10: object = maker->makeObject(params);
11: }
12: return object;
13: }
14:
3.PRIORITIZING FACTORIES,優先工廠:前面我們一直都在使用關聯容器 map 存儲 key 與 對象工廠之間的連繫,我們知道 map 中的 key 是唯一的,也就是說我們前面假定了 key(params) 與 對象工廠(maker)之間存在 1-1 的關係。但實際中經常可能會出現不只有一個對象工廠可以創建某個對象,這時我們只需要將 map 改成 multimap,並在創建對象時設置一定的優先策略即可。其中,ITK 中的對象工廠使用的便是 multimap,一個對象可能會有多個工廠可以創建。
我們創建一個新類 PriorityMaker 表示優化查找策略,它從對象工廠列表中查找可以創建指定對象優先級最高的對象工廠:
1: //Priority Factory
2: template <class Object>
3: class PriorityMaker
4: : public Maker
5: {
6: public:
7: static Object* newObject(istream& params)
8: {
9: string className;
10: params >> className;
11:
12: //定位第一個可以創建 className 對象的對象工廠
13: const_iterator iter = registry.lower_bound(className);
14:
15: //繼續在 multimap 中查找,直至某個對象工廠不能創建 className 對象實例
16: //並進行簡單的比較,選擇優先級最高的對象工廠
17: const_iterator tmp = iter;
18: while ( ++tmp != registry.end() &&
19: (*tmp).first == className)
20: {
21: if (*(*iter).second < *(*tmp).second)
22: iter = tmp;
23: }
24:
25: //最後使用選擇到的對象工廠創建對象實例
26: MakerPtr maker = (*iter).second;
27: return maker->makeObject(params);
28: }
29: }
30:
4.SINGLETON FACTORIES,單件工廠:當我們只有一個選擇的時候,會需要單件工廠,就如同單件對象一樣。單件工廠的查找策略非常簡單,因爲在某一時間只有一個對象工廠會被註冊。如果發生不止一個對象工廠被註冊的錯誤,我們原則是:使用最後一個註冊的對象工廠。
1: //Singleton Factory
2: template <class Object>
3: class SingletonMaker : public Maker
4: {
5: public:
6: static Object* newObject()
7: {
8: //rbegin:如果出現多個工廠註冊的錯誤,我們選擇最後一個
9: MakerPtr maker =
10: (*registry.rbegin()).second;
11: return maker->makeObject();
12: }
13: protected:
14: SingletonMaker()
15: : Maker
16: };
17: