超级工厂模式的实现与应用

在软件开发中,对象的创建和管理是一个常见且重要的问题。传统的对象工厂实现要求用户在运行时从共同的基类派生出要创建的对象,或者使用多个工厂来创建不同类型的对象。然而,这些方法在处理复杂的对象关系时可能会变得繁琐。为了解决这个问题,可以引入一种称为“超级工厂”的设计模式。

超级工厂是一种对象工厂,它能够创建任何类型的实例,并且可以通过它们的基类接口返回这些实例。这种模式特别适用于持久性框架。与传统的对象工厂实现相比,超级工厂提供了一个统一的接口来在运行时创建不同类型的对象。

要使用超级工厂模式,首先需要将一个文件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::Register函数)。然而,如上文所述,每个产品需要有关其派生产品的信息(以委托创建);而不是有关其基产品的信息。因此,没有“RegisterBase”函数;相反,有一个“RegisterDerived”函数。因此,产品实际上是与基类的工厂注册的(通过Factory::RegisterDerived函数)。这就是每个工厂知道其派生产品的方式(尽管对用户来说,他似乎是在注册类及其基类)。

优点:

  • 非常容易使用和维护。
  • 提供了一个统一的接口来创建所有对象。
  • 不需要RTTI。
  • 不需要对象是多态的。
  • 不会给对象增加开销(在时间和空间方面)。
  • 支持原始类型(float, unsigned int等)。
  • 支持(实例化的)模板类型。
  • 可以在不修改现有类的情况下为其提供工厂服务。

缺点:

  • 提供的实现仅支持具有公共无参构造函数的对象。
  • 如果类层次结构发生变化,需要一些维护(但通常,这不是经常发生的事情)。
  • 使用线性搜索来查找所需的对象类型。所需时间取决于目标类型和要返回的类型之间的层次结构“距离”。通常这不应该是一个问题,除非有非常深的类层次结构。

请注意,在上述代码示例中,为了清晰起见,省略了由工厂分配的对象的删除;在现实世界的代码中,将希望在使用后删除它们,或者使用std::auto_ptr(或智能指针)自动为删除它们。

在随附的源代码中,还有两个示例。一个展示了工厂如何处理菱形继承层次结构,另一个展示了工厂如何处理模板类型。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485