简介 工厂方法,通常又被称作虚构造函数,给一个ID,就可以产出一个对象。 了解设计模式的人都知道这样一份臭名昭著的实现: // --------------------------------------------------------------------------------- // Shape.h enum ShapeType { ShapeType_Line, ShapeType_Triangle, }; class Shape { public: virtual ~Shape() {} virtual void Draw() = 0; static Shape* Create( ShapeType shapeType ); }; // --------------------------------------------------------------------------------- // Shape.cpp #include "Line.h" #include "Triangle.h" Shape* Shape::Create( ShapeType shapeType ) { switch ( shapeType ) { case ShapeType_Line: return new Line; case ShapeType_Triangle: return new Triangle; default: return NULL; } } 缺点 一个显著的缺点是新类型难于扩展: 如果你新增了一个类型Rectangle,就要改动 1. Shape.h - 在enum里加一个类型ID 2. Shape.cpp - 增加一个头文件#include,增加一个switch case分支 对于Shape.h的改动的影响面可能超乎你的想象: 1. 所有派生于Shape的类实现,因为包含到Shape.h,将不得不重新编译 2. 所有Shape类的用户同样也得重新编译 另外这种做法无法封装成库,因为我们不可能因为用户新增了类型而去修改lib文件。 所以要具备可扩展性,用户新增的类型就不能影响Shape.h和Shape.cpp。要怎做呢?Loki库给了我们很好的展示,基本做法像下面这样: 解决方案 // --------------------------------------------------------------------------------- // Shape.h typedef const char* ShapeType; typedef Shape* (*Creator)(); class Shape { public: virtual ~Shape() {} virtual void Draw() = 0; }; class ShapeFactory { public: static ShapeFactory& Instance() { static ShapeFactory instance; return instance; } Shape* Create( ShapeType shapeType ); bool RegisterShape( ShapeType shapeType, Creator creator ); private: ShapeFactory() {} Map< ShapeType, Creator > shapeCreators; }; // --------------------------------------------------------------------------------- // Shape.cpp Shape* ShapeFactory::Create( ShapeType shapeType ) { Creator creator = shapeCreators.Find( shapeType ); if ( creator == NULL ) { return NULL; } return creator(); } bool RegisterShape( ShapeType shapeType, Creator creator ) { Creator creator = shapeCreators.Find( shapeType ); if ( creator == NULL ) { shapeCreators.Insert( shapeType, creator ); return true; } return false; } // --------------------------------------------------------------------------------- // Line.cpp namespace { Shape* Create() { return new Line; } const bool RegisterShape__ = ShapeFactory::Instance().RegisterShape( "Line", Create ); } // --------------------------------------------------------------------------------- // Triangle.cpp namespace { Shape* Create() { return new Triangle; } const bool RegisterShape__ = ShapeFactory::Instance().RegisterShape( "Triangle", Create ); } 如果增加新类,只需在新类里增加类似上面这段就行了。可以把这断代码做成宏放在Shape.h里面。 #define REGISTER_SHAPE( className ) / namespace { / Shape* Create() { return new className; } / const bool RegisterShape__ = ShapeFactory::Instance().RegisterShape( #className, Create );/ } 细节讨论 1. 关于ID a)可以采用任何你喜欢的类型做为ID,但必须保证其唯一性。字符串通常是个不错的选择(如果你用整数,那么最好用GUID生成器产生,因为你并不知道其他子类的ID是什么)。 b)采用const char*作为ID,有一定的风险:如果你的类打算从文件中序列化产生,那么序列化出来的字符串并不位于全局静态存储区,直接对其做 == 操作会产生错误。解决方案是使用string替代const char*。 2. ShapeFactory是个单件,关于单件的详细讨论,可以参考: 单件和仿单件的6种做法 3. 创建函数typedef Shape* (*Creator)(); 此乃上述做法之关键:首先将ID映射为创建函数,再由此函数产生对象。将函数保存起来并于稍后调用,这也是Command模式之核心观念。(《C++设计新思维》p100) 一些替代的方案可供考虑: a)传统方案,用函数对象代替函数指针: struct Creator { Shape* operator() { return new Line; } } b)使用模板以避免创建子类(详见《设计模式》p74-75): struct Creator { virtual Shape* Create() = 0; } template <class T> struct StdCreator : public Creator { virtual Shape* Create() { return T; } } 与Loki同行 如果你的工程使用Loki,那么一切都简单了,像下面这样: // ---------------------------------------------------------------------------------------------------------------------------------- // Shape.h #ifndef Shape_H_INCLUDED_ #define Shape_H_INCLUDED_ #include <string> #include <loki/Singleton.h> #include <loki/Factory.h> class Shape { public: virtual ~Shape() {} virtual void Draw() = 0; }; typedef Loki::SingletonHolder< Loki::Factory< Shape, std::string > > ShapeFactory; #define REGISTER_SHAPE( className ) / namespace { / Shape* Create() { return new className; } / const bool RegisterShape__ = ShapeFactory::Instance().Register( #className, Create ); / } #endif // Shape_H_INCLUDED_ // ---------------------------------------------------------------------------------------------------------------------------------- // Line.h #ifndef Line_H_INCLUDED_ #define Line_H_INCLUDED_ #include "Shape.h" class Line : public Shape { public: virtual void Draw(); }; #endif // Line_H_INCLUDED_ // ---------------------------------------------------------------------------------------------------------------------------------- // Line.cpp #include <iostream> #include "Line.h" REGISTER_SHAPE( Line ) void Line :: Draw() { std::cout << "Line :: Draw()" << std::endl; } // ---------------------------------------------------------------------------------------------------------------------------------- // main.cpp #include "Shape.h" int main() { Shape* line = ShapeFactory::Instance().CreateObject( "Line" ); line->Draw(); delete line; return 0; } 参考资料 《设计模式》chapter3.3 《C++设计新思维》chapter8 |
[作者hjsunj 原帖:http://blog.csdn.net/hjsunj/archive/2008/01/07/2028597.aspx] |
工厂方法(Factory Method)之追根究底
最新推荐文章于 2024-07-26 23:39:00 发布