原文作者:曹骥 接上一帖,消除switch和case语句: 由名字创建对象其实的有挺强的背景,那就是软件工程学中的对象持久性问题。本文讨论的方法也是持久性的一个可能实现。废话少说,看看C++是怎么做的。 动态创建当然要用指针。我们不希望在程序中出现硬编码: Shape * pShape = new Circle; 那就用函数/对象包装一下。函数包装简单、高效一些。于是有Circle类的成员函数: Shape *createInstance() {return new Circle;} 我们需要用这个函数去创建Circle对象,同时只有Circle对象创建以后才可以调用createInstance()。这是一对矛盾。解决的办法是将createInstance()写成静态函数。于是Shpae和Circle类长的是这个样子: class Shape{ public: virtual void Draw() = 0; }; class Circle : public Shape{ public: void Draw() { std::cout << "Drawing a Circle......Done" << std::endl; } static Shape *createInstance() {return new Circle;} }; 指向createInstance()的函数指针当然要放在工厂类里面,同时要和创建对象的名字关联起来。std::map的键-值组合当然是最佳人选。我们还有如下定义: typedef Shape *(*FactoryFunction)(); FactoryFunction就是一个指向函数的指针,而这个函数返回一个Shape *类型。如果你对FactoryFunction有疑问,可以看看关于函数指针的书。 有了FactoryFunction,我们来定义工厂类中的map: std::map<std::string,FactoryFunction> m_FactoryFunctions; 这样的map对象是把名字(string类型)和函数指针作为一个组合。向工厂登记产品的时候,函数可以写成这样: void ShapeFactory::Register(std::string name, FactoryFunction instanceFunction){ m_FactoryFunctions[name] = instanceFunction; } 登记Circle类的创建函数: Register("circle", &Circle::createInstance); ShapeFactory类中由名字得到函数指针可以写成这样: Shape * ShapeFactory::getInstance(std::string name) { if (m_FactoryFunctions.count(name)) return m_FactoryFunctions[name](); else return NULL; } map.count()的用法可以看看STL的手册。ShapeFacoty的完整定义如下: class ShapeFactory { public: static void Register(std::string name, FactoryFunction instanceFunction) {m_FactoryFunctions[name] = instanceFunction;}; static Shape * getInstance (std::string name) { if (m_FactoryFunctions.count(name)) return m_FactoryFunctions[name](); else return NULL; } private: static std::map<std::string,FactoryFunction> m_FactoryFunctions; }; std::map<std::string,FactoryFunction> ShapeFactory::m_FactoryFunctions; 其中Register()和getInstance()是静态函数,m_FactoryFunctions是静态成员变量。之所以这样做是避免创建ShapeFactory对象,以简化编程。同时便于用单例(Singleton)模式实现ShapeFactory。 这些完成以后,就可以向工厂登记产品和使用产品了。看看下面代码: void someFunction() { ShapeFactory::Register("circle", & Circle::createInstance); Shape * pShape = NULL; pShape = ShapeFactory::getInstance("circle"); if (NULL == pShape) { std::cout << "can't find the product in the factory" << std::endl; delete pShape; } else { pShape->Draw(); delete pShape; } } 怎么样,还不算复杂吧。有人会认为函数指针太不“面向对象”了,应该用其他方法。工厂模式的实现方法很多的,这里只是做一个例子。 注意观察上面的代码。这里已经没有了switch/case语句。而且ShapeFactory类和Shape类的定义和实现是完全封闭的。新添加的产品类将不改变工厂类和基类的代码。这是一个标志性的变化。当用户添加一个新产品的时候,他可以不管工厂已经有什么产品,也不用写丑陋的switch/case语句了。具体的产品是在运行时刻决定的。这样的工厂模式才具备实用价值,而不是简单的、对象/类之间的堆砌和关联。 当我们添加一个新产品的时候,比如Triangle,可以这样做: 1) 定义Triangle类: class Triangle : public Shape { public: void Draw() { std::cout << "Drawing a Triagnle......Done" << std::endl; } static Shape *createInstance() {return new Triangle;} }; 2) 在合适的地方登记Triangle: ShapeFactory::Register("triangle", & Triangle::createInstance); 3) 给出名字,就可以创建Triangle对象了: pShape = ShapeFactory::getInstance("triangle"); 综上所述,我们的第一个目标已经实现:由名字创建对象。同时我们也详细讨论的工厂模式的实现。注意我们的工厂只是一个ConcreteFactory。在上面的新产品添加过程中,能不能省略第二步?能否实现产品在工厂中的自动登记?这将是我们要讨论的第二个问题。 目前的实现对JAVA和PYTHON程序员来说不是难事。但是产品自动登记可就不简单了。有谁可以在1周内做出来,我就转行学JAVA和PYTHON。 最后贴出完整代码(VC6和GCC都编译通过): #pragma warning (disable:4786) #include <iostream> #include <map> #include <string> class Shape; typedef Shape *(*FactoryFunction)(); class ShapeFactory { public: static void Register(std::string name, FactoryFunction instanceFunction) {m_FactoryFunctions[name] = instanceFunction;}; static Shape * getInstance(std::string name) { if (m_FactoryFunctions.count(name)) return m_FactoryFunctions[name](); else return NULL; } private: static std::map<std::string,FactoryFunction> m_FactoryFunctions; }; std::map<std::string,FactoryFunction> ShapeFactory::m_FactoryFunctions; class Shape { public: virtual void Draw() = 0; }; class Circle : public Shape { public: void Draw() { std::cout << "Drawing a Circle......Done" << std::endl; } static Shape *createInstance() {return new Circle;} }; class Triangle : public Shape { public: void Draw() { std::cout << "Drawing a Triagnle......Done" << std::endl; } static Shape *createInstance() {return new Triangle;} }; int main() { ShapeFactory::Register("circle", & Circle::createInstance); ShapeFactory::Register("Triangle", & Triangle::createInstance); Shape * pShape = NULL; pShape = ShapeFactory::getInstance("circle"); if (NULL == pShape) { std::cout << "can't find the product in the factory" << std::endl; delete pShape; } else { pShape->Draw(); delete pShape; } return 0; }

评论