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

Create new instance of a derived class depending on a parameter

我想根据文件扩展名创建程序的新实例。不幸的是我不能改变界面,因为文件名是一个命令行参数。另外,如果我想添加新程序,我不喜欢这样,我必须处理 if-else 语句。我考虑过用于存储的映射(字符串,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);
    }
}

这个怎么样?

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 class 并在那里进行初始化。