Создать новый экземпляр производного класса в зависимости от параметра - PullRequest
1 голос
/ 06 ноября 2019

Я хочу создавать новые экземпляры программ в зависимости от расширения файла. К сожалению, я не могу изменить интерфейс, потому что filename является аргументом командной строки. Кроме того, мне не нравится, что если я хочу добавить новую программу, мне приходится иметь дело с операторами if-else. Я думал о картах для хранения (string, type_index), но, видимо, этот подход не работает с шаблонами. Кроме того, я попытался разобраться с std :: enable_if, передав функцию сравнения строк constexpr, но это, очевидно, тоже не работает. Я полагаю, что могу как-то расширить последний подход, создав набор функций и выбрав одну в зависимости от сравнения строк. По сути, я хочу добиться расширяемости и соответствовать принципам 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 Ответ

2 голосов
/ 06 ноября 2019

Как насчет этого?

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 в отдельной единице перевода и выполнить там инициализацию.

...