あなたが探しているのは、OOPの一般的なパターンです。 デザインパターン (Gang of Fourの本)では、これをコマンドパターンと呼んでいます。
通常、テンプレートは必要ありません。すべてが実行時に解析およびディスパッチされるため、動的ポリモーフィズム(仮想関数)がおそらくより適切な選択です。
別の答えでは、ラファエルバプティスタは基本的なデザインを提案しました。彼のデザインをより完全なものに変更する方法は次のとおりです。
コマンドオブジェクトとCommandDispatcher
コマンドは、Command
クラスのサブクラスによって処理されます。CommandDispatcher
コマンドは、コマンド文字列の基本的な解析(基本的には、スペースでの分割、場合によっては引用符で囲まれた文字列の処理など)を処理するオブジェクトによってディスパッチされます。
システムはのインスタンスをに登録し、のCommand
各CommandDispatcher
インスタンスをCommand
コマンド名(std::string
)に関連付けます。関連付けはstd::map
オブジェクトによって処理されますが、ハッシュテーブル(またはキーと値のペアを関連付けるための同様の構造)で置き換えることができます。
class Command
{
public:
virtual ~Command(void);
virtual void execute(FILE* in, const std::vector<std::string>& args) = 0;
};
class CommandDispatcher
{
public:
typedef std::map<std::string, Command*> CommandMap;
void registerCommand(const std::string& commandName, Command* command)
{
CommandMap::const_iterator cmdPair = registeredCommands.find(commandName);
if (cmdPair != registeredCommands.end())
{
// handle error: command already registered
}
else
{
registeredCommands[commandName] = command;
}
}
// possibly include isRegistered, unregisterCommand, etc.
void run(FILE* in, const std::string& unparsedCommandLine); // parse arguments, call command
void dispatch(FILE* in, const std::vector<std::string>& args)
{
if (! args.empty())
{
CommandMap::const_iterator cmdPair = registeredCommands.find(args[0]);
if (cmdPair == registeredCommands.end())
{
// handle error: command not found
}
else
{
Command* cmd = cmdPair->second;
cmd->execute(in, args);
}
}
}
private:
CommandMap registeredCommands;
};
解析やその他の詳細は省略しましたが、これはコマンドパターンのかなり一般的な構造です。std::map
コマンド名とコマンドオブジェクトを関連付けるハンドルに注目してください。
コマンドの登録
この設計を利用するには、システムにコマンドを登録する必要があります。シングルトンパターンCommandDispatcher
を使用して、または別の中央の場所でインスタンス化する必要があります。main
次に、コマンドオブジェクトを登録する必要があります。これを行うにはいくつかの方法があります。私が好む方法は、より詳細に制御できるため、各モジュール(関連するコマンドのセット)に独自の登録機能を提供させることです。たとえば、「ファイルIO」モジュールがある場合、次の関数がありますfileio_register_commands
。
void fileio_register_commands(CommandDispatcher* dispatcher)
{
dispatcher->registerCommand( "readfile", new ReadFileCommand );
dispatcher->registerCommand( "writefile", new WriteFileCommand );
// etc.
}
ここReadFileCommand
に、およびは、目的の動作を実装するWriteFileCommand
サブクラスです。Command
fileio_register_commands
コマンドが使用可能になる前に、必ず呼び出す必要があります。
このアプローチは、動的にロードされるライブラリ(DLLまたは共有ライブラリ)で機能するように作成できます。コマンドを登録する関数が、モジュールの名前に基づいて規則的なパターンになっていることを確認してください。XXX_register_commands
ここXXX
で、は、たとえば小文字のモジュール名です。共有ライブラリまたはDLLをロードした後、コードはそのような関数が存在するかどうかを判断し、それを呼び出すことができます。