【问题标题】:How can I improve my simple factory pattern?如何改进我的简单工厂模式?
【发布时间】:2020-02-16 19:36:47
【问题描述】:

现在我使用单例工厂来创建对象。我最初想注册每个新类,所以我不需要更改现有的工厂类,但我现在将 CproductA 类公开给用户。如何设计?

Coliru Viewer

三个问题如下:

  1. CProductA 类和 CProductB 类向用户公开。
  2. 我需要提前创建对象。
  3. m_Map 需要是一个全局变量。
 #include <map>
 #include <iostream>
 using namespace std;

 //Abstract
 class CProduct
 {
  public:
     virtual void Operation() = 0;
 };

 class CProductA:public CProduct
 {
 public:
     virtual void Operation()
     {
        cout << "Operation A" << endl;
     }
 };

 class CProductB:public CProduct
 {
 public:
     virtual void Operation()
     {
        cout << "Operation B" << endl;
     }
 };

 //Simple Factory
 map <int, CProduct*> m_Map;

 class CSimpleFactory
 {
 public:
    void RegisteProduct(int nId, CProduct* pProduct)
    {
       m_Map[nId] = pProduct;
    }
    void CreateProduct(int nId)
    {
       m_Map[nId]->Operation();
    }
    static CSimpleFactory* GetInstance()
    {
       if(m_Instance)
       {
           m_Instance = new CSimpleFactory;
       }
       return m_Instance;
    }
    void Release()
    {
       if(m_Instance)
       {
           delete m_Instance;
       }
    }
 private:
    static CSimpleFactory* m_Instance;
 };

    CSimpleFactory* CSimpleFactory::m_Instance = NULL;


 int main()
 {
     CSimpleFactory* pSimpleFactory = CSimpleFactory::GetInstance();
     pSimpleFactory->RegisteProduct(1,new CProductA);
     pSimpleFactory->RegisteProduct(2,new CProductB);
     pSimpleFactory->CreateProduct(1);
 }

【问题讨论】:

    标签: c++ design-patterns


    【解决方案1】:

    我重新访问了Factory method pattern,发现我的怀疑是合理的:

    工厂模式的意思是通过一定的标识来创建一个注册类的实例。 OP 只是将标识符(索引)映射到存储对象的方法调用。

    我稍微修改了 OP 的示例:

    #include <iostream>
    #include <map>
    #include <string>
    #include <vector>
    

    首先是工厂的代码:

    // factory
    
    class CProduct {
      protected:
        CProduct() = default;
        virtual ~CProduct() = default;
        CProduct(const CProduct&) = delete;
        CProduct& operator=(const CProduct&) = delete;
    
      public:
        virtual void operation() = 0;
    };
    
    class CFactory {
      public:
        typedef std::string Id;
    
        static CFactory& get() // Meyers Singleton
        { static CFactory factory;
          return factory;
        }
      private:
        // a table mapping IDs to products
        typedef std::map<Id, CProduct*(*)()> Table;
        Table _table;
      private:
        CFactory() = default;
        ~CFactory() = default;
        CFactory(const CFactory&) = delete;
        CFactory& operator=(const CFactory&) = delete;
    
      public:
        void registerClass(const Id &id, CProduct*(*pFuncNew)())
        {
          _table[id] = pFuncNew;
        }
    
        CProduct* create(const Id &id) const
        {
          const Table::const_iterator iter = _table.find(id);
          return iter != _table.end()
            ? (iter->second)() // call function to create instance
            : nullptr; // ERROR! Unknown ID.
        }
    };
    
    // helper function template
    template <typename CLASS>
    CProduct* newProduct() { return new CLASS; }
    

    然后是一些需要在工厂创建的产品:

    // products
    
    class CProductA: public CProduct {
      public:
        virtual void operation() override
        {
          std::cout << "CProductA::operation()\n";
        }
    };
    
    class CProductB: public CProduct {
      public:
        virtual void operation() override
        {
          std::cout << "CProductB::operation()\n";
        }
    };
    
    void initProducts(CFactory &factory)
    {
      factory.registerClass("A", newProduct<CProductA>);
      factory.registerClass("B", newProduct<CProductB>);
    }
    

    最后是使用工厂和产品的应用程序:

    // application
    
    int main()
    {
      CFactory &factory = CFactory::get();
      initProducts(factory); // to prevent this call is hard to achieve
      // input sequence
      const std::string input[] = {
        "A", "B", "A", "A", "B", "C"
      };
      // create instances for input by factory
      std::cout << "Create products:\n";
      std::vector<CProduct*> products;
      for (const std::string &id : input) {
        CProduct *const pProd = factory.create(id);
        if (!pProd) {
          std::cerr << "Unknown product type '" << id << "'!\n";
        } else products.push_back(pProd);
      }
      // do something with created products
      std::cout << "Use products:\n";
      for (CProduct *const pProd : products) {
        pProd->operation();
      }
    }
    

    输出:

    Create products:
    Unknown product type 'C'!
    Use products:
    CProductA::operation()
    CProductB::operation()
    CProductA::operation()
    CProductA::operation()
    CProductB::operation()
    

    Live Demo on coliru

    关于 OP 的具体问题:

    1. CProductA 类和 CProductB 类向用户公开。

    不一定。想象// factory 之后的部分是一个库,// products 之后的部分是另一个库。应用程序(// application 之后的第三部分)不需要“知道”// products 的任何内容,void initProducts() 除外。因此,其他所有内容都不需要从库中导出。

    这对于某种插件实现来说甚至是好的。

    1. 我需要提前创建对象。

    没有。在我的实现中,只需要一个创建函数。

    1. m_Map 需要是一个全局变量。

    如果工厂实现为单例,则它必须是全局的。这对应用程序程序员来说可能很方便,在这种和类似的情况下我更喜欢这样做。

    同样,CFactory 可能被实例化为非全局的。但是,在创建工厂之后,还必须注册要创建的类(在我的情况下调用 initProducts())。这就是为什么我之前说可能很方便(对于应用程序开发人员)为工厂提供单例。

    注意:

    为了将创建函数存储在CFactory 中,我使用了函数指针CProduct* (*pFunc)()——一个指向没有任何参数的函数的指针,返回指向CProduct 的指针。或者,我可以使用std::function&lt;CProduct*()&gt;(在生产代码中,我可能使用过)。虽然函数指针可能只针对普通函数(在此示例中就足够了),但std::function 可能会针对具有该签名的任何可调用对象,包括仿函数和捕获 lambda。在我们的生产代码中,这显示出很有价值,因为我们生产的一些对象依赖于额外的参数,这些参数必须在工厂的类注册中绑定。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-06-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多