我正在制作音译工具。我有两个模块lexer和翻译器。 Lexer从输入文本中生成标记。根据当前选择的语言,我必须调用适当的翻译例程。
我想出了几个想法来做到这一点。第一个是创建一个名为base_translator
的基类,并提供虚拟方法(translate()
),每个翻译者都必须覆盖它。现在创建一个工厂translator_factory
并使用语言名称调用create()
。该工厂将返回适当的实例。
但这似乎过度工程化了。所以我提出了另一种方法,我有一个类似下面的结构。
struct translator
{
const char* name;
void (*fp)();
};
只保留一个语言名称和一个可以处理它的函数指针。用法是,
static translator translators[] = {
{"first", first},
{"second", second}
};
const char* language = /* */;
for(int i = 0; i < 2; i++) {
translator *t = translators + i;
if(strcmp(t->name, language) == 0) {
t->fp();
break;
}
}
这种方法非常简单易维护。但我想知道,这是解决问题的最佳方法吗?你有什么建议让这更好吗?
任何帮助都会很棒。
答案 0 :(得分:5)
这通常是抽象类和虚函数的一个受欢迎的用例。我不明白为什么你认为它是“过度工程”...... o_O
重点是定义一个契约,以便您的代码可以轻松扩展,“主代码”不必担心实际的实现细节。
答案 1 :(得分:3)
你的第二种方法对于你来说可能看起来只有两个项目(对我来说它不是......),但从长远来看,它更容易出错并且难以维护。无论何时添加新语言,都需要至少在两个位置触摸代码。 (相信我:即使你现在看起来不太可能,它几乎不可避免地会发生......)如果你忘记更新循环边界,你的代码中就会出现一个无声的错误。此外,这个实现比多态的慢得多:你需要迭代数组并在每次调用之前比较字符串(而不是在vtable中查找指针)。
我肯定会使用工厂。除了上述优点外,它还是一种众所周知的设计模式,使其更易于理解。因此,追随你维护代码的人不会诅咒你那么多; - )
更新:工厂也可以返回一个函数指针。在这种情况下,我仍然会在内部使用functors,因为这些是完全成熟的类,具有上面列出的所有好处。还有一个,没有明确提到:它们(作为真实对象)可以存储状态,而普通函数则不能。这可能会在以后产生重大影响,并大大简化您的设计。当然,一旦您拥有了工厂界面,您就可以轻松地将其产品的内部表示从普通函数更改为仿函数(并返回)。
答案 2 :(得分:2)
任何一种解决方案都适用于您所说的问题。如果问题空间增长,工厂可能会更好。例如,如果将来需要另一个依赖于语言的类或函数,则可以扩展工厂以返回它。函数指针表需要更多的更改来处理类似的事情。我要去工厂。问题域总是在增长。