工厂设计模式如何处理不同长度的构造函数?

gre*_*eep 5 c++ factory

我想要一个类,它根据我传递的字符串创建不同类型的对象。根据我的研究,这最好地描述了工厂设计模式。我成功地实现了它,但遇到了一个设计问题:我不知道如何创建具有不同长度构造函数的对象。

我们以一个名为 Pet 的抽象父类为例。其中有 3 个孩子:鱼、猫和狗。它们都继承了 Pet 的重量和颜色,因此它们都包含在它们的构造函数中。但是一条鱼可能需要多个鳍和一个关于它是否是咸水鱼的布尔值。这是一个 4 参数构造函数。猫想要腿的数量。那是3个参数。狗可能有腿、品种以及是否与其他狗相处良好等 5 个参数。

在 C++ 中,我知道没有任何反射,因此最常见的做法似乎只是声明一个字符串到函数指针的映射,其中函数指针指向一个如下所示的函数:

    template<typename T> Pet* createObject(int weight, std::string color) {return new T(weight, color);}
Run Code Online (Sandbox Code Playgroud)

同样,我不确定如何在调用中填充更多参数而不影响其他对象构造函数的调用。

我可以想到两种解决方法:创建新函数来接受不同数量的参数,或者为构造函数创建超过一定大小的默认参数。

解决方法 1 似乎有点过多,具体取决于我有多少个不同的参数大小。

解决方法 2 似乎忽略了构造函数的全部要点,因为我将被迫在调用构造函数后分配数据。

还有其他更好的解决方法吗?

pet*_*ohn 3

您可以使用可变参数模板和完美转发。

template<typename T, typename... Args>
Pet* createObject(Args&&... args) {
    return new T(std::forward<Args>(args)...);
}
Run Code Online (Sandbox Code Playgroud)

但是,由于任何指针都可以转换为其基类,因此如果此函数返回T*. 此外,使用裸指针并不明智,因为您必须手动删除它们。最好使用shared_ptrunique_ptr。对于这些类,已经有类似的工厂方法:make_sharedmake_unique(后者仅在 C++14 中)。或者,如果您的编译器不支持 C++11,那么您可以使用Boost中的shared_ptr和。make_shared

当然,当您在编译时知道需要创建什么类型时,此解决方案就有效。如果你必须在运行时决定,那么整个问题就必须从不同的方向考虑,就好像你不知道你要创建什么类型,那么你就无法知道要给它们什么参数,除了所有类型通用的参数。在这种情况下,您需要的是抽象工厂模式。幸运的是,C++(至少从 C++11 开始)提供了一种实现此模式的方法,而无需创建大量的类。例如,假设您必须创建某个派生自Pet. 宠物的实际种类、大小和其他属性是在其他地方决定的,而宠物的名称是在创建时决定的。然后,你需要一个像这样的工厂:

typedef std::function<std::shared_ptr<Pet>(const std::string& name)> PetFactory;
Run Code Online (Sandbox Code Playgroud)

在某个时刻,你决定要创建一个Dog(我将实际创建参数的含义留给你想象)。

PetFactory petFactory =
        [](const std::string& name) {
            return std::make_shared<Dog>(name, 12, "brown", 23.5);
        }
Run Code Online (Sandbox Code Playgroud)

当你实际创建它时,你需要做的就是调用工厂:

std::shared_ptr<Pet> pet = petFactory("Pet Name");
Run Code Online (Sandbox Code Playgroud)

  • 这意味着在编译时指定创建的对象类型。通常使用工厂模式,您希望在运行时指定创建的对象。如果犯了错误,带有对象类型运行时规范的简单可变参数方法通常会导致 UB。 (2认同)