根据参数创建派生类的新实例

Posted

技术标签:

【中文标题】根据参数创建派生类的新实例【英文标题】:Create new instance of a derived class depending on a parameter 【发布时间】:2019-11-06 11:53:27 【问题描述】:

我想根据文件扩展名创建新的程序实例。不幸的是,我无法更改界面,因为文件名是命令行参数。另外,如果我想添加新程序,我不喜欢这样,我必须处理 if-else 语句。我考虑过用于存储 (string, type_index) 的地图,但显然这种方法不适用于模板。此外,我尝试通过传递 constexpr 字符串比较函数来处理 std::enable_if ,但这显然也不起作用。我想我可以通过创建一堆函数并根据字符串比较选择一个函数来以某种方式扩展后一种方法。基本上,我想要的是实现可扩展性并符合 SOLID 原则。

std::shared_ptr<Program> Program::fromFile(const Path& file)
    auto extension = file.extension();

    if(extension == "cpp")
        return std::make_shared<CppProgram>(file);
     else if(extension == "py")
        return std::make_shared<PythonProgram>(file);
     else if(extension == "java")
        return std::make_shared<JavaProgram>(file);
     else if(extension == "go")
        return std::make_shared<GoProgram>(file);
     else 
        throw std::runtime_error("unknown file extension " + extension);
    

【问题讨论】:

您已经回答了您的问题。这正是你的做法。任何其他变体都将基本相同,但更加模糊。 使用工厂注册(创作者的)。要扩展只需在工厂注册一个新的创建者。工厂只是询问每个创建者“你能从这个文件中创建吗”它是否可以返回结果,如果它不能尝试下一个。 【参考方案1】:

这个怎么样?

struct Maker 
  virtual shared_ptr<Program> make(const Path& file) = 0;
;

map<string, unique_ptr<Maker>> g_makers;

std::shared_ptr<Program> Program::fromFile(const Path& file) 
  auto extension = file.extension();
  auto it = g_makers.find(extension);
  if(it == g_makers.end())
    throw std::runtime_error("unknown file extension " + extension);
  return it->second->make(file);        


// probably in a separate file:

struct CppMaker : Maker 
  shared_ptr<Program> make(const Path& file) override 
    return make_shared<CppProgram>(file);
  
;

__attribute__((constructor))
static void initCppMaker() 
  g_makers["cpp"].reset(new CppMaker());

现在您需要做的就是决定何时以及如何将各种 Maker 实现注册到 g_makers。您可以使用特定于编译器的语法(如 GCC 的 __attribute__((constructor)))或其他初始化技术来执行此操作。您可以在单独的翻译单元中定义每个 Maker 派生类并在那里进行初始化。

【讨论】:

你能举一个使用这种技术注册的例子吗?

以上是关于根据参数创建派生类的新实例的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法根据派生类的可能性值范围来限制对象参数?

简单工厂模式

如何从指向多态基类的指针复制/创建派生类实例?

cython c++ 类继承不能实例化派生类

如何键入提示 Python 函数返回从超类派生的任何类的实例?

C++ 派生模板类:访问实例的受保护成员