总结工厂方法

Wrapping up factory methods

本文关键字:方法 工厂      更新时间:2023-10-16

我有 2 个structs,其构造函数接受不同数量和类型的传入参数。例如

struct A:Base
{
   A(int i, char c){}
};
struct B:Base
{
   B(char c){}
};

而且我还有一个工厂方法来创建这两个structs的对象,

struct F
{
   Base* Do(int i)
   {
      if (i==0)
      {
         return new A(i,'c');
      }
      else
      {
         return new B('c'); 
      }
   }  
};

现在我试着像这样改善我的Factory F

struct F
{
   template<int i, class X>
   Base* Do();
   template<class X>
   Base* Do<0>()
   {
      return new X(i, 'c');
   }
   template<class X>
   Base* Do<1>()
   {
      return new X('c');
   }
};

我对这种方法唯一不感兴趣的是,如果我有很多结构A,B,C,...,我必须编写很多专门的模板,但我只喜欢模板,不喜欢return new X(...)

所以我的问题是:

  1. 如何最小化长代码但仍使用模板?
  2. 如何停止返回指向基类的指针?动态分配 sux,我更喜欢工厂堆栈上的缓慢而温和的经典分配器。
  3. 我喜欢使用可变参数模板来表示我的structs ' ctors 的参数。

std::any和可变参数模板将是你的朋友。Yuu可以在std::any中存储任何东西。any_cast会有点问题。

请参阅以下工作代码:

#include <iostream>
#include <map>
#include <utility>
#include <any>

// Some demo classes ----------------------------------------------------------------------------------
struct Base {
    Base(int d) : data(d) {};
    virtual ~Base() { std::cout << "Destructor Basen"; }
    virtual void print() { std::cout << "Print Basen"; }
    int data{};
};
struct Child1 : public Base {
    Child1(int d, std::string s) : Base(d) { std::cout << "Constructor Child1 " << d << " " << s << "n"; }
    virtual ~Child1() { std::cout << "Destructor Child1n"; }
    virtual void print() { std::cout << "Print Child1: " << data << "n"; }
};
struct Child2 : public Base {
    Child2(int d, char c, long l) : Base(d) { std::cout << "Constructor Child2 " << d << " " << c << " " << l << "n"; }
    virtual ~Child2() { std::cout << "Destructor Child2n"; }
    virtual void print() { std::cout << "Print Child2: " << data << "n"; }
};
struct Child3 : public Base {
    Child3(int d, long l, char c, std::string s) : Base(d) { std::cout << "Constructor Child3 " << d << " " << l << " " << c << " " << s << "n"; }
    virtual ~Child3() { std::cout << "Destructor Child3n"; }
    virtual void print() { std::cout << "Print Child3: " << data << "n"; }
};

using UPTRB = std::unique_ptr<Base>;

template <class Child, typename ...Args>
UPTRB createClass(Args...args) { return std::make_unique<Child>(args...); }
// The Factory ----------------------------------------------------------------------------------------
template <class Key, class Object>
class Factory
{
    std::map<Key, std::any> selector;
public:
    Factory() : selector() {}
    Factory(std::initializer_list<std::pair<const Key, std::any>> il) : selector(il) {}
    template<typename Function>
    void add(Key key, Function&& someFunction) { selector[key] = std::any(someFunction); };
    template <typename ... Args>
    Object create(Key key, Args ... args) {
        if (selector.find(key) != selector.end()) {
            return std::any_cast<std::add_pointer_t<Object(Args ...)>>(selector[key])(args...);
        }
        else return nullptr;
    }
};
int main()
{
    Factory<int, UPTRB> factory{
        {1, createClass<Child1, int, std::string>},
        {2, createClass<Child2, int, char, long>}
    };
    factory.add(3, createClass<Child3, int, long, char, std::string>);

    // Some test values
    std::string s1(" Hello1 "); std::string s3(" Hello3 ");
    int i = 1;  const int ci = 1;   int& ri = i;    const int& cri = i;   int&& rri = 1;
    UPTRB b1 = factory.create(1, 1, s1);
    UPTRB b2 = factory.create(2, 2, '2', 2L);
    UPTRB b3 = factory.create(3, 3, 3L, '3', s3);
    b1->print();
    b2->print();
    b3->print();
    b1 = factory.create(2, 4, '4', 4L);
    b1->print();
    return 0;
}