C++ 类生成管理器, 摆脱if/else

在开发中,经常遇到根据配置内容,生成对应的类, 例如:

name = "Product_A", 则生成产品A的类, 

name = "Product_B", 则生成产品B的类,

产品A, 产品B都继承于同一个基类。

比较简单且常用的一个写法是这样的:

#include <iostream>
using namespace std;

class Base
{
 public:
  Base(){
      //...};
  };
};

class A : public Base
{
 public:
  A(){
      //...};
  };
};

class B : public Base
{
 public:
  B(){
      //...};
  };
};

Base* creator(std::string name)
{
  if (name == "Product_A")
  {
    return new A();
  }
  else if (name == "Product_B")
  {
    return new B();
  }

  return nullptr;
}

这样写,虽然能满足需求,但是不够优雅,对于每一个产品大类(例如另外一种产品, 都继承于Base2),都要写这样一坨if/else。

 

有没有更优雅的写法呢? 利用模板跟可变参数实现一个创建管理器:

#include <map>

using namespace std;

/* B : 基类
 * T : B的派生类
 * P : T的构造函数所需参数
 */

template <class B, class... P>
class CreateFctBase
{
 public:
  virtual ~CreateFctBase() = default;
  virtual B* create(P... args) = 0;
};

template <class B, class T, class... P>
class CreateFct : public CreateFctBase<B, P...>
{
 public:
  virtual ~CreateFct() = default;
  B* create(P... args)
  {
    return new T(args...);
  }
};

template <class B, class... P>
class Factory
{
 public:
  Factory() = default;
  virtual ~Factory()
  {
    for (auto& m : m_mapStr2Obj)
    {
      delete m.second;
    }
    m_mapStr2Obj.clear();
  }

 public:
  void regist(std::string strName, CreateFctBase<B, P...>* creator)
  {
    m_mapStr2Obj[strName] = creator;
  }

  B* create(std::string strName, P... args)
  {
    auto it = m_mapStr2Obj.find(strName);
    if (it != m_mapStr2Obj.end())
    {
      return it->second->create(args...);
    }
    return nullptr;
  }

 private:
  std::map<std::string, CreateFctBase<B, P...>*> m_mapStr2Obj;
}; 

思路是, 通过模板的参数, 保存派生类的类型, 将字符串->类, 通过map<string, 模板类> 存储。

使用如下:

#include <iostream>

#include "creater.h"

class A
{
 public:
  virtual void print()
  {
    std::cout << a << std::endl;
  }

 public:
  int a = 1;
};

class A1 : public A
{
  virtual void print()
  {
    A::print();
    std::cout << a1 << std::endl;
  }

 public:
  int a1 = 11;
};

class B
{
 public:
  B(std::string str)
  {
  }

 public:
  virtual void print()
  {
    std::cout << a << std::endl;
  }

 public:
  int a = 2;
};

class B1 : public B
{
 public:
  B1(std::string str) : B(str)
  {
  }
  virtual void print()
  {
    B::print();
    std::cout << a1 << std::endl;
  }

 public:
  int a1 = 22;
};

class C
{
 public:
  C(int i, std::string str) : a(i), name(str)
  {
  }

 public:
  virtual void print()
  {
    std::cout << a << std::endl;
  }

 public:
  int a = 0;
  std::string name;
};

class C1 : public C
{
 public:
  C1(int i, std::string str) : C(i, str)
  {
  }
  virtual void print()
  {
    std::cout << "this is C1:" << std::endl;
    std::cout << "--- members:" << a << "  " << a1 << name << std::endl;
  }

 public:
  int a1 = 22;
};

class C2 : public C
{
 public:
  C2(int i, std::string str) : C(i, str)
  {
  }
  virtual void print()
  {
    std::cout << "this is C2:" << std::endl;
    std::cout << "--- members:" << a << "  " << a12 << name << std::endl;
  }

 public:
  int a12 = 222;
};

int main()
{
  Factory<A> AFact;
  AFact.regist("a1", new CreateFct<A, A1>());
  auto pA = AFact.create("a1");
  pA->print();

  Factory<B, std::string> BFact;
  BFact.regist("a1", new CreateFct<B, B1, std::string>());
  auto pB = BFact.create("a1", "name");
  pB->print();

  Factory<C, int, std::string> CFact;
  CFact.regist("c1", new CreateFct<C, C1, int, std::string>());
  CFact.regist("c2", new CreateFct<C, C2, int, std::string>());
  auto pC1 = CFact.create("c1", 1, "name_c1");
  auto pC2 = CFact.create("c2", 1, "name_c2");
  pC1->print();
  pC2->print();
  return 0;
}

 

这样基本摆脱了if/else的创建模式。但是像 

CFact.regist("c1", new CreateFct<C, C1, int, std::string>());
CFact.regist("c2", new CreateFct<C, C2, int, std::string>());

这种,写法很冗余,且容易出错。继续优化一下, 使用类内置结构体, 共享类的模板参数。优化后如下:

#include <map>

using namespace std;

/* B : 基类
 * T : B的派生类
 * P : T的构造函数所需参数
 */

template <class B, class... P>
class CreateFctBase
{
 public:
  virtual ~CreateFctBase() = default;
  virtual B* create(P... args) = 0;
};

template <class B, class T, class... P>
class CreateFct : public CreateFctBase<B, P...>
{
 public:
  virtual ~CreateFct() = default;
  B* create(P... args)
  {
    return new T(args...);
  }
};

template <class B, class... P>
class Factory
{
 public:
  Factory() = default;
  virtual ~Factory()
  {
    for (auto& m : m_mapStr2Obj)
    {
      delete m.second;
    }
    m_mapStr2Obj.clear();
  }

  template <class T>
  struct Registor
  {
    Registor(Factory* pFactory, std::string strName)
    {
      pFactory->regist(strName, new CreateFct<B, T, P...>());
    }
  };

 public:
  void regist(std::string strName, CreateFctBase<B, P...>* creator)
  {
    m_mapStr2Obj[strName] = creator;
  }

  B* create(std::string strName, P... args)
  {
    auto it = m_mapStr2Obj.find(strName);
    if (it != m_mapStr2Obj.end())
    {
      return it->second->create(args...);
    }
    return nullptr;
  }

 private:
  std::map<std::string, CreateFctBase<B, P...>*> m_mapStr2Obj;
};

相比一开始的实现, 只多了一个内置结构体:

  template <class T>
  struct Registor
  {
    Registor(Factory* pFactory, std::string strName)
    {
      pFactory->regist(strName, new CreateFct<B, T, P...>());
    }
  };

使用时, 会简单很多,如下:

#include <iostream>

#include "creater.h"

class ProductManager : public Factory<C, int, std::string>
{
 public:
  ProductManager() = default;
  virtual ~ProductManager() = default;

 public:
  void regist()
  {
    Registor<C1>(this, "c1");
    Registor<C2>(this, "c2");
    // CFact.regist("c1", new CreateFct<C, C1, int, std::string>());
    // CFact.regist("c2", new CreateFct<C, C2, int, std::string>());
  }
};

int main()
{
  ProductManager CFact;
  CFact.regist();
  auto pC1 = CFact.create("c1", 1, "name_c1");
  auto pC2 = CFact.create("c2", 1, "name_c2");
  pC1->print();
  pC2->print();
  return 0;
}

  这样,如果再增加一个产品大类Base2, 则只需要写一个管理器 ProductManager2 继承于 Factory, 然后用Registor注册即可。


posted @ 2021-02-21 19:02  upupon  阅读(235)  评论(0编辑  收藏  举报