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: