There is some nasty legacy code.
std::string xxx = GetCommand(); // get "CommandX";
if (xxx == "Command1")
{
return new Command1();
}
else if (xxx == "Command2")
{
return new Command2();
}
...
else if (xxx == "Command100")
{
return new Command100();
}
I want to improve this code structure.
There were too many comparison. So I put them to a map.
for (int i = 0; i < GetCommandCount(); ++i)
{
// key is a command string
// value is a function pointer which creates it's instance
map.insert(command, a function pointer);
}
// then
ICommand* pCommand = map.getInstance(command);
But this way has to make additional function every time if new command comes.
Yes, the functions might be reasonable. But all the functions just be return new CommandNNN();
I guess there is the way to remove the duplication.
How do you think?
Since all the functions are return new CommandNNN();
, you can use a template function:
template <class T>
CommandBase* createCommand() {
return new T();
}
and bind to this function in your map:
map.insert(std::make_pair("Command1", &createCommand<Command1>));
map.insert(std::make_pair("Command2", &createCommand<Command2>));
map.insert(std::make_pair("Command3", &createCommand<Command3>));
This lets you avoid creating a new function for each command. However, there would still be some duplication in the map.insert
-statements. This could be further reduced by using macros, if that's your cup of tea:
#define INSERT(cmd) map.insert(std::make_pair(#cmd, &createCommand<cmd>));
INSERT(Command1);
INSERT(Command2);
INSERT(Command3);
#undef INSERT
or
#define INSERT(n) map.insert(std::make_pair("Command" #n, &createCommand<Command ## n>));
INSERT(1);
INSERT(2);
INSERT(3);
#undef INSERT
I suspect that you can even get the preprocessor to do some counting for you, but that's outside of my domain.
Applying even more macros, as well as some global state, both of which are frowned upon by many, you can get even tighter coupling:
#include <map>
#include <string>
#include <cassert>
class CommandBase {};
static std::map<std::string, CommandBase* (*)()> g_commandMap;
template <class C>
CommandBase* createCommand() {
return new C();
}
class CommandRegistrer {
public:
CommandRegistrer(const std::string& name, CommandBase* (*instantiator)()) {
g_commandMap.insert(std::make_pair(name, instantiator));
}
};
#define COMMAND_CLASS(n) \
class Command##n; \
CommandRegistrer g_commandRegistrer##n("Command" #n, createCommand<Command##n>); \
class Command##n : public CommandBase
COMMAND_CLASS(1) { /* implementation here */ };
COMMAND_CLASS(2) { /* implementation here */ };
int main() {
assert(g_commandMap.find("Command1") != g_commandMap.end());
}
If you are using C++11, you can use inline lambdas to do it so that everything is in one place:
class Object
{
};
class Command1 : public Object
{
};
// etc
typedef std::map<std::string, std::function<Object*()>> FunctionMap;
typedef std::pair<std::string, std::function<Object*()>> FunctionPair;
FunctionMap funcMap;
funcMap.insert(FunctionPair("Command1", []()
{
return new Command1();
}));
Why not just make a static array
static struct cmdthing {
const char *cmd;
void (*fun)();
} commands[] = {
{..,..},
{..,..},
...
};
for(const cmdthing *p=commands;p<commands+sizeof(commands)/sizeof(*commands);++p)
if(!strcmp(p->cmd,cmd)) return (*(p->fun))();
Or something like that?
You can put your map as private member of your factory like:
CommandFactory{
private:
std::map< std::string, ICommand*> m_commands;
public:
CommandFactory();
ICommand* getInstance()const;
virtual ~CommandFactory();
};
In the constructor register all your actions like:
CommandFactory(){
m_commands.insert( std::pair< std::string, ICommand*>("commandName", new Command) );
}
ICommand is an interface so make a virtual method invoke()
class ICommand{
public:
ICommand();
virtual bool invoke()=0;
virtual ~ICommand();
};
But normally an simple factory could be anough.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With