zoukankan      html  css  js  c++  java
  • Factory

    来个绘图经典的例子吧。基类是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;

    }

     
  • 相关阅读:
    将excel文件的内容导入sql server数据库的方法
    DataGridView 中的 CheckBox
    TextBox的文本换行问题
    寻找TreeView中的项(c#)
    关于C#的几个问题
    远程通信
    人脸搜索项目开源了:人脸识别(M:N)Java版
    人脸识别中的重要环节对齐之3D变换Java版(文末附开源地址)
    揭秘人脸对齐之3D变换Java版(文末附开源地址)
    阿里云视觉智能开放平台的人脸1:N搜索的开源替代Java版(文末附开源地址)
  • 原文地址:https://www.cnblogs.com/sunkang/p/2038844.html
Copyright © 2011-2022 走看看