来个绘图经典的例子吧。基类是Shape,子类是CircleTriangle等等。肯定有人写过这样的代码:

 

enum SHAPE_DEFINE

{CIRCLE, TRIANGLE, RECTANGLE };

 

someFunction()

{

      

       SHAPE_DEFINE shape_variable;

       ……

Shape * shap;

       switch(shape_variable)

       {

       case CIRCLE:

              shape = new Circle;

              break;

       case TRIANGLE:

              shape = new Trigangle;

              break;

       .....

       }

       //do something with shape

       .....

}

 

我们因该尽量避免在工厂模式的Factory类出现类似代码。原因是:使用case语句不利于以后的扩充和维护。当添加新的产品的时候,我们最好不要修改Factory类中的代码,包括添加新产品的头文件和修改创建新产品的case语句。我们希望的代码如下

 

someFunction()

{

       Shape * shap ShapeFactory::getInstance(“Circle”);

       //do something with shape

}

 

要达到以上目标,我们将讨论在C++中两个重要实现:

 

1)      由名字创建对象

2)      新产品在工厂模式中的自动登记功能(auto-register)

由名字创建对象其实的有挺强的背景,那就是软件工程学中的对象持久性问题。本文讨论的方法也是持久性的一个可能实现。废话少说,看看C++是怎么做的。

 

动态创建当然要用指针。我们不希望在程序中出现硬编码:

 

Shape * pShape = new Circle;

 

那就用函数/对象包装一下。函数包装简单、高效一些。于是有Circle类的成员函数:

 

Shape *createInstance() {return new Circle;}

 

我们需要用这个函数去创建Circle对象,同时只有Circle对象创建以后才可以调用createInstance()。这是一对矛盾。解决的办法是将createInstance()写成静态函数。于是ShpaeCircle类长的是这个样子:

 

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。在上面的新产品添加过程中,能不能省略第二步?能否实现产品在工厂中的自动登记?这将是我们要讨论的第二个问题

 

目前的实现对JAVAPYTHON程序员来说不是难事。但是产品自动登记可就不简单了。有谁可以在1周内做出来,我就转行学JAVAPYTHON

 

最后贴出完整代码(VC6GCC都编译通过):

 

#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;

}

 
 posted on 2006-08-15 16:03  sun_kang  阅读(243)  评论(0编辑  收藏  举报