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;

    }

     
  • 相关阅读:
    git线上操作
    IDEA快捷方式
    Java 四种线程池
    java 获取当前天之后或之前7天日期
    如何理解AWS 网络,如何创建一个多层安全网络架构
    申请 Let's Encrypt 通配符 HTTPS 证书
    GCE 部署 ELK 7.1可视化分析 nginx
    使用 bash 脚本把 AWS EC2 数据备份到 S3
    使用 bash 脚本把 GCE 的数据备份到 GCS
    nginx 配置 https 并强制跳转(lnmp一键安装包)
  • 原文地址:https://www.cnblogs.com/sunkang/p/2038844.html
Copyright © 2011-2022 走看看