如何改善我的简单工厂模式?

时间:2019-08-15 02:56:06

标签: c++ design-patterns

现在,我使用单例工厂创建对象。我本来想向每个新类注册,所以不需要更改现有的工厂类,但是现在我向用户公开了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);
 }

1 个答案:

答案 0 :(得分:2)

我重新访问了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类向用户公开。
  2.   

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

这甚至对某种插件实现都是有益的。

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

不。在我的实现中,只需要一个create函数。

  
      
  1. m_Map必须是全局变量。
  2.   

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

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

注意:

要将创建函数存储在CFactory中,我使用了函数指针CProduct* (*pFunc)() –指向函数的指针,没有任何参数返回指向CProduct的指针。或者,我可以使用std::function<CProduct*()>(在生产代码中,我可能已经使用过)。尽管函数指针仅可以寻址普通函数(在此示例中足够),但是std::function可以寻址具有该签名的任何可调用对象,包括函子和捕获lambda。在我们的生产代码中,这显示出了宝贵的价值,因为我们的某些生产对象依赖于工厂类注册中必须绑定的额外参数。