Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如何重构此结构,if-else if*100_C++_Design Patterns - Fatal编程技术网

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!");
...