C++ 如何重构此结构,if-else if*100
有一些令人讨厌的遗留代码C++ 如何重构此结构,if-else if*100,c++,design-patterns,C++,Design Patterns,有一些令人讨厌的遗留代码 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(); } 我想改进此
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();
}
我想改进此代码结构。有太多的比较。所以我把它们放在地图上
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);
for(int i=0;i
但是,如果有新命令出现,这种方法必须在每次都执行附加功能。
是的,功能可能是合理的。但是所有的函数都是返回新的CommandNNN()代码>我想有办法消除重复
你觉得怎么样?为什么不制作一个静态数组呢
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))();
static struct cmdthing{
常量字符*cmd;
空虚(*乐趣)();
}命令[]={
{..,..},
{..,..},
...
};
for(const-cmdthing*p=commands;pcmd,cmd))返回(*(p->fun))();
或者类似的东西?如果您使用的是C++11,您可以使用内联lambdas来执行此操作,以便将所有内容都放在一个位置:
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();
}));
类对象
{
};
类Command1:公共对象
{
};
//等
typedef std::map FunctionMap;
typedef std::pair FunctionPair;
功能图功能图;
插入(函数对(“Command1”)和[]()
{
返回新的Command1();
}));
您可以将地图作为工厂的私人成员,如:
CommandFactory{
private:
std::map< std::string, ICommand*> m_commands;
public:
CommandFactory();
ICommand* getInstance()const;
virtual ~CommandFactory();
};
但是通常情况下,一个简单的工厂可能是不可能的。因为所有函数都是返回新命令nnn()代码>,您可以使用模板功能:
template <class T>
CommandBase* createCommand() {
return new T();
}
或
#定义插入(n)映射。插入(std::make_pair(“Command”#n,&createCommand));
插入(1);
插入(2);
插入(3);
#未定义插入
我怀疑你甚至可以让预处理器为你做一些计数,但这超出了我的范围
应用更多的宏和一些全局状态(这两种状态都是许多人不喜欢的),可以获得更紧密的耦合:
#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());
}
#包括
#包括
#包括
类CommandBase{};
静态std::map g_commandMap;
模板
CommandBase*createCommand(){
返回新的C();
}
类命令注册器{
公众:
CommandRegistrer(const std::string&name,CommandBase*(*实例化器)(){
插入(std::make_pair(名称、实例化器));
}
};
#定义命令类(n)\
类命令##n\
commandregister g#u commandregister###n(“命令”#n,createCommand)\
类命令##n:公共CommandBase
命令_类(1){/*此处实现*/};
命令类(2){/*此处实现*/};
int main(){
断言(g_commandMap.find(“Command1”)!=g_commandMap.end();
}
只需解析字符串以返回int,然后通过开关即可。这应该是快速和小。如果需要,案例
s可以很容易地生成。这个例子很明显:
int ToCommandID(const std::string& CommandX) { evaluate and return X as an int }
Command* NewCommand() {
const std::string xxx(GetCommand()); // get "CommandX";
const int commandID(ToCommandID(xxx));
switch (commandID) {
case 1 : return new Command1();
case 2 : return new Command2();
case 3 : return new Command3();
case 4 : return new Command4();
case 5 : return new Command5();
case 6 : return new Command6();
case 7 : return new Command7();
case 8 : return new Command8();
case 9 : return new Command9();
case 10 : return new Command10();
case 11 : return new Command11();
case 12 : return new Command12();
case 13 : return new Command13();
case 14 : return new Command14();
...
default : {
assert(0 && "oh no!");
...
对不起,今天没有特别的语言功能。当然,您可以通过宏运行此操作并减少字符数,也可以将其标记为生成的代码,并在2分钟内完成。找到编写代码的人并开枪!这一次,做一个住在隔壁用电锯的精神病患者!我认为每个命令的函数听起来合理且可读。拥有所有这些函数会给你带来问题吗?还是仅仅因为你不喜欢它?只是庆幸他没有想到“嘿,我可以用宏来让代码更干净!”@Magnus,我想是的。但我认为这是代码重复。我正在寻找一种方法来消除重复。到目前为止,你的回答是我想要的最好的方式。我在等其他想法。为什么不std::vector
和std::find
?这不是你在这里做的吗?不,不是。首先,我没有把编译时可以做的事情推迟到运行时。其次,我避免引入与简单任务无关的结构。第三,整个逻辑只需要几行代码,它们不会分散在您的代码库中。std::find
在这里是可以的,尽管它不会简化事情,但您需要使用lambda(如果可用)或发明自己的函数进行比较,它仍然会更慢,更清晰。你得到的唯一好处是你可以声称你使用了算法,这不是编程的直接目标。不过,你不必同意我的观点,编写代码的方法总是不止一种。我认为代码会更清晰,但这是我个人的观点。但是,我怀疑你的说法,而且std::find
会更慢。你甚至可以用一个巨大的初始值设定项列表替换所有insert
调用来填充映射,这样就可以将映射创建移到编译时,从而允许编译器进行更多优化:)@rubenvb:map创建将在程序启动时进行,而不是在编译时进行,不过。(尽管“静态构造函数”对于C++19来说是个不错的概念!)Kerrek:我不明白为什么在构造函数中初始化的const std::map
必须在运行时构造。它可能是嵌入在可执行文件中的1和0的恒定内存块?或者至少在我看来应该是:)。标准中是否有任何规则阻止这种优化?它与字符串文字不是很相似吗?它们也只是嵌入内存的位置,不是吗?@rubenvb我想这在“仿佛”规则下是允许的。但是,不要指望会有很多编译器实现它,这里的问题是,无论是否调用该命令,您都在创建该命令。这可能是一个问题,也可能不是。我认为如果你做了一个新的动作,你会使用它,否则为什么要保留死代码?我不确定asker的这些命令对象实际上包含什么,但是如果它们创建了一些东西,比如要执行的网络资源,该怎么办?如果它们可以用不同的参数以不同的方式实例化呢?如果我们一开始就创建了它们,我们如何向它们传递额外的信息?最好在需要的时候创建它们,imho。所有的对象都将被实例化
map.insert(std::make_pair("Command1", &createCommand<Command1>));
map.insert(std::make_pair("Command2", &createCommand<Command2>));
map.insert(std::make_pair("Command3", &createCommand<Command3>));
#define INSERT(cmd) map.insert(std::make_pair(#cmd, &createCommand<cmd>));
INSERT(Command1);
INSERT(Command2);
INSERT(Command3);
#undef INSERT
#define INSERT(n) map.insert(std::make_pair("Command" #n, &createCommand<Command ## n>));
INSERT(1);
INSERT(2);
INSERT(3);
#undef INSERT
#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());
}
int ToCommandID(const std::string& CommandX) { evaluate and return X as an int }
Command* NewCommand() {
const std::string xxx(GetCommand()); // get "CommandX";
const int commandID(ToCommandID(xxx));
switch (commandID) {
case 1 : return new Command1();
case 2 : return new Command2();
case 3 : return new Command3();
case 4 : return new Command4();
case 5 : return new Command5();
case 6 : return new Command6();
case 7 : return new Command7();
case 8 : return new Command8();
case 9 : return new Command9();
case 10 : return new Command10();
case 11 : return new Command11();
case 12 : return new Command12();
case 13 : return new Command13();
case 14 : return new Command14();
...
default : {
assert(0 && "oh no!");
...