在软件开发中,对象的创建和管理是一个常见且重要的问题。传统的对象工厂实现要求用户在运行时从共同的基类派生出要创建的对象,或者使用多个工厂来创建不同类型的对象。然而,这些方法在处理复杂的对象关系时可能会变得繁琐。为了解决这个问题,可以引入一种称为“超级工厂”的设计模式。
超级工厂是一种对象工厂,它能够创建任何类型的实例,并且可以通过它们的基类接口返回这些实例。这种模式特别适用于持久性框架。与传统的对象工厂实现相比,超级工厂提供了一个统一的接口来在运行时创建不同类型的对象。
要使用超级工厂模式,首先需要将一个文件SuperFactory.h添加到项目中。然后,为了给一个类添加工厂支持,需要在该类的实现文件(.cpp)中“注册”它。为此,提供了一些宏来处理最常见的用例。
宏的语法如下:
SF_Register_Type[_Base(n)]( SF_Concrete/SF_Abstract,
例如,要注册一个没有基类的抽象类A,会使用:
SF_Register_Type( SF_Abstract, A );
要注册一个有两个基类A和B的具体类C,会使用:
SF_Register_Type_Base2( SF_Concrete, C, A, B );
注册完类之后,就可以使用工厂的Create方法来创建所需的对象。
让通过一个简单的例子来说明如何使用超级工厂模式。假设有以下在各自的头文件中定义的类:
在A.h中定义:
struct A { virtual void Print() = 0; virtual ~A() { } };
在B.h中定义:
struct B : public A { virtual void Print() { cout << "In B::Print" << endl; } virtual ~B() { } };
在C.h中定义:
struct C : public B { virtual void Print() { cout << "In C::Print" << endl; } virtual ~C() { } };
为了添加工厂支持,需要在各自的实现文件中注册每个类:
在A.cpp中:
SF_Register_Type( SF_Abstract, A );
在B.cpp中:
SF_Register_Type_Base1( SF_Concrete, B, A );
在C.cpp中:
SF_Register_Type_Base1( SF_Concrete, C, B );
注册完成后,可以使用工厂来创建对象:
A *pObj1; B *pObj2; A *pObj3;
if (SuperFactory::Create("B", pObj1)) { pObj1->Print(); }
if (SuperFactory::Create("C", pObj2)) { pObj2->Print(); }
if (SuperFactory::Create("C", pObj3)) { pObj3->Print(); }
需要注意的是,尽管类A和C可能由两个互不了解对方实现的人实现,但工厂已经间接地“告诉”了A是C的基类。因此,工厂能够创建类型为C的对象,并通过类型为A的接口返回它们,就像上面示例中的pObj3一样。
另一个简单的例子是,假设有以下在各自的头文件中定义的类:
在A.h中定义:
struct A { virtual void Print() = 0; virtual ~A() { } };
在B.h中定义:
struct B { virtual void Print() = 0; virtual ~B() { } };
在C.h中定义:
struct C : public A, public B { virtual void Print() { cout << "In C::Print" << endl; } virtual ~C() { } };
为了添加工厂支持,需要在各自的实现文件中注册每个类:
在A.cpp中:
SF_Register_Type( SF_Abstract, A );
在B.cpp中:
SF_Register_Type( SF_Abstract, B );
在C.cpp中:
SF_Register_Type_Base2( SF_Concrete, C, A, B );
注册完成后,可以使用工厂来创建对象:
A *pA; B *pB;
if (SuperFactory::Create("C", pA)) { pA->Print(); }
if (SuperFactory::Create("C", pB)) { pB->Print(); }
需要注意的是,C类同时继承自A类和B类,因此工厂可以创建类型为C的对象,并通过A类或B类的基接口返回它们。
超级工厂也可以创建原始类型(前提是它们已经注册)。假设用户希望通过工厂创建float和unsigned int对象,他首先需要像通常一样注册它们(在某个实现文件中):
SF_Register_Type( SF_Concrete, float );
SF_Register_Type( SF_Concrete, unsigned int );
注册完成后,就可以根据需要创建这些原始类型的对象了:
float *pFloat;
unsigned int *pUint;
if (SuperFactory::Create("float", pFloat)) { /* Do something with pFloat */ }
if (SuperFactory::Create("unsigned int", pUint)) { /* Do something with pUint */ }
超级工厂的工作原理类似于在《Modern C++ Design》一书中描述的工厂。对于每种特定的产品类型(具体/抽象),都存在一个模板化的工厂类实例来处理它。每个工厂只能创建它设计用来处理的产品类型的对象(前提是它处理的产品类型不是抽象的)。然而,它也有能力将创建任务委托给处理派生产品的工厂。
当被要求创建一个产品时,给定一个产品标识符,工厂首先检查请求的产品是否是它设计用来处理的。如果是,它简单地使用用户提供的产品创建函数,并返回创建的对象给用户。但如果产品标识符不被工厂识别,它就将创建任务委托给处理派生产品类型的工厂,希望产品能被其中之一识别。这个过程会递归地进行,直到产品层次结构中的一个工厂识别了标识符(这意味着它知道如何创建该类型的产品),或者该产品层次结构中的工厂没有一个识别标识符。
如果产品标识符不被产品层次结构中的任何一个工厂识别,那么: (a) 要创建的产品不属于该层次结构(即,它是一个基产品,或者是一个不相关的产品)。 (b) 要创建的产品没有(或错误地)注册到其关联的工厂。 (c) 层次结构中的一个产品没有(或错误地)注册,从而破坏了创建委托的“链”。
如果产品层次结构中的一个工厂识别了产品标识符,它就创建产品并将其返回给调用者,这将是直接的基产品工厂。这个过程会递归地进行,直到产品最终到达用户。
当用户注册一个产品及其直接的基类时,幕后会发生一些巧妙的事情。产品直接与其工厂注册(通过Factory
优点:
缺点:
请注意,在上述代码示例中,为了清晰起见,省略了由工厂分配的对象的删除;在现实世界的代码中,将希望在使用后删除它们,或者使用std::auto_ptr(或智能指针)自动为删除它们。
在随附的源代码中,还有两个示例。一个展示了工厂如何处理菱形继承层次结构,另一个展示了工厂如何处理模板类型。