Это действительно кажется довольно тривиальным, так что я предполагаю, что мы пропустили некоторые требования.
Используйте Memoization , чтобы избежать вычисления результата более одного раза. Это должно быть сделано в рамках.
Вы могли бы использовать некоторую блок-схему, чтобы определить, как сделать передачу информации от одного модуля к другому ... но самый простой способ состоит в том, чтобы каждый модуль напрямую вызывал тех, от кого зависит. С запоминанием это не стоит много, так как, если оно уже вычислено, у вас все в порядке.
Поскольку вам нужно иметь возможность запускать любой модуль, вам нужно дать им идентификаторы и зарегистрировать их где-нибудь, чтобы найти их во время выполнения. Есть два способа сделать это.
- Exemplar: вы получаете уникальный образец этого типа модуля и выполняете его.
- Фабрика: вы создаете модуль запрошенного типа, запускаете его и выбрасываете.
Недостатком метода Exemplar
является то, что если вы выполните модуль дважды, вы начнете не с чистого состояния, а с состояния, в котором его оставило последнее (возможно, неудачное) выполнение. Для запоминания это может можно рассматривать как преимущество, но в случае неудачи результат не вычисляется (ург), поэтому я рекомендовал бы против него.
Так как ты ...?
Начнем с завода.
class Module;
class Result;
class Organizer
{
public:
void AddModule(std::string id, const Module& module);
void RemoveModule(const std::string& id);
const Result* GetResult(const std::string& id) const;
private:
typedef std::map< std::string, std::shared_ptr<const Module> > ModulesType;
typedef std::map< std::string, std::shared_ptr<const Result> > ResultsType;
ModulesType mModules;
mutable ResultsType mResults; // Memoization
};
Это действительно очень простой интерфейс. Однако, так как мы хотим новый экземпляр модуля каждый раз, когда мы вызываем Organizer
(чтобы избежать проблемы повторного входа), нам нужно будет работать с нашим Module
интерфейсом.
class Module
{
public:
typedef std::auto_ptr<const Result> ResultPointer;
virtual ~Module() {} // it's a base class
virtual Module* Clone() const = 0; // traditional cloning concept
virtual ResultPointer Execute(const Organizer& organizer) = 0;
}; // class Module
А теперь все просто:
// Organizer implementation
const Result* Organizer::GetResult(const std::string& id)
{
ResultsType::const_iterator res = mResults.find(id);
// Memoized ?
if (res != mResults.end()) return *(it->second);
// Need to compute it
// Look module up
ModulesType::const_iterator mod = mModules.find(id);
if (mod != mModules.end()) return 0;
// Create a throw away clone
std::auto_ptr<Module> module(it->second->Clone());
// Compute
std::shared_ptr<const Result> result(module->Execute(*this).release());
if (!result.get()) return 0;
// Store result as part of the Memoization thingy
mResults[id] = result;
return result.get();
}
И простой пример модуля / результата:
struct FooResult: Result { FooResult(int r): mResult(r) {} int mResult; };
struct FooModule: Module
{
virtual FooModule* Clone() const { return new FooModule(*this); }
virtual ResultPointer Execute(const Organizer& organizer)
{
// check that the file has the correct format
if(!organizer.GetResult("CheckModule")) return ResultPointer();
return ResultPointer(new FooResult(42));
}
};
А из основного:
#include "project/organizer.h"
#include "project/foo.h"
#include "project/bar.h"
int main(int argc, char* argv[])
{
Organizer org;
org.AddModule("FooModule", FooModule());
org.AddModule("BarModule", BarModule());
for (int i = 1; i < argc; ++i)
{
const Result* result = org.GetResult(argv[i]);
if (result) result->print();
else std::cout << "Error while playing: " << argv[i] << "\n";
}
return 0;
}