我希望您对以下伪代码提出建议。请建议我如何改进它,无论我是否可以使用一些设计模式。
// i'm receiving a string containing : id operation arguments
data = read(socket);
tokens = tokenize(data," "); // tokenize the string based on spaces
if(tokens[0] == "A") {
if(tokens[1] == "some_operation") {
// here goes code for some_operation , will use the remaining tokens as arguments for function calls
}
else if(tokens[1] == "some_other_operation") {
// here goes code for some_other_operation , will use the remaining tokens
}
...
else {
// unknown operation
}
}
else if(tokens[0] == "B") {
if(tokens[1] == "some_operation_for_B") {
// do some operation for B
}
else if(tokens[1] == "yet_another_operation") {
// do yet_another_operation for B
}
...
else {
// unknown operation
}
}
我希望你明白这一点。问题是我有大量的 id ,每个都有自己的操作,我认为有10个代码的屏幕包含很多有点难看if's 和 else if 。
答案 0 :(得分:14)
为每个实现通用接口的ID创建一个类。基本上是Strategy pattern IIRC。
所以你要调用(伪)代码,如:
StrategyFactory.GetStrategy(tokens[0]).parse(tokens[1..n])
答案 1 :(得分:8)
首先写下你支持的语法,然后编写代码来支持它。
使用BNF符号非常适合。使用Spirit库作为代码部分非常简单。
Command := ACommand | BCommand
ACommand := 'A' AOperation
AOperation := 'some_operation' | 'some_other_operation'
BCommand := 'B' BOperation
BOperation := 'some_operation_for_B' | 'some_other_operation_for_B'
这很容易转化为灵魂解析器。每个生产规则都将成为一个单行,每个结束符号都会被转换为一个函数。
#include "stdafx.h"
#include <boost/spirit/core.hpp>
#include <iostream>
#include <string>
using namespace std;
using namespace boost::spirit;
namespace {
void AOperation(char const*, char const*) { cout << "AOperation\n"; }
void AOtherOperation(char const*, char const*) { cout << "AOtherOperation\n"; }
void BOperation(char const*, char const*) { cout << "BOperation\n"; }
void BOtherOperation(char const*, char const*) { cout << "BOtherOperation\n"; }
}
struct arguments : public grammar<arguments>
{
template <typename ScannerT>
struct definition
{
definition(arguments const& /*self*/)
{
command
= acommand | bcommand;
acommand = chlit<char>('A')
>> ( a_someoperation | a_someotheroperation );
a_someoperation = str_p( "some_operation" ) [ &AOperation ];
a_someotheroperation = str_p( "some_other_operation" )[ &AOtherOperation ];
bcommand = chlit<char>('B')
>> ( b_someoperation | b_someotheroperation );
b_someoperation = str_p( "some_operation_for_B" ) [ &BOperation ];
b_someotheroperation = str_p( "some_other_operation_for_B" )[ &BOtherOperation ];
}
rule<ScannerT> command;
rule<ScannerT> acommand, bcommand;
rule<ScannerT> a_someoperation, a_someotheroperation;
rule<ScannerT> b_someoperation, b_someotheroperation;
rule<ScannerT> const&
start() const { return command; }
};
};
template<typename parse_info >
bool test( parse_info pi ) {
if( pi.full ) {
cout << "success" << endl;
return true;
} else {
cout << "fail" << endl;
return false;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
arguments args;
test( parse( "A some_operation", args, space_p ) );
test( parse( "A some_other_operation", args, space_p ) );
test( parse( "B some_operation_for_B", args, space_p ) );
test( parse( "B some_other_operation_for_B", args, space_p ) );
test( parse( "A some_other_operation_for_B", args, space_p ) );
return 0;
}
答案 2 :(得分:4)
您可以查看“表驱动方法”(如“代码完成”,第2版,第18章中所述)。 我认为这是what Cheery describes. 这样做的好处是易于扩展。您只需要在表格中添加一些条目。该表可以在运行时进行硬编码甚至加载。
与Epaga's suggestion类似,您也可以尝试通过多态来解决这个问题,让专门的类为不同的情况执行操作。这里的缺点是你必须在发生变化的情况下编写新的类。
答案 3 :(得分:2)
您希望将其拆分为多个功能,每个ID一个,每个操作一个。
我通常使用的指南是屏幕高度。如果我无法在屏幕上完全适应功能,我会开始考虑将事情分开。这样你就不需要滚动来看看函数的去向。正如我所说,这是一个指导原则,而不是规则,但我发现控制结构更为实际。
如果您想采用OO方法并将其转换为一堆类,如果您看到优势,欢迎您这样做。但请注意与之相关的所有管道。你可能希望保持简单。
戴夫
答案 4 :(得分:2)
我已经看到这个问题的解决方案运行良好:函数的哈希表。
在编译时,为每个支持的操作创建Perfect Hash Function,并且操作与要调用的函数相关联(函数指针是散列中的值,命令字符串是键)。
在运行时期间,通过使用命令字符串在哈希表中查找函数来调用命令功能。然后调用该函数通过引用传递“data”字符串。然后,每个命令函数根据其规则解析剩余的字符串......此时策略模式也适用。
使代码像状态机一样工作,这是(IMHO)最简单的方法来处理网络代码。
答案 5 :(得分:1)
创建功能图。然后你会得到如下代码:
consumed_count = token_mapper[tokens[0]](tokens)
remove amount of consumed tokens according to the return value and repeat.
尽管如此,我还是不了解你的方法,你会写一种难以处理和不灵活的语言。想一想:参数数量的微小差异会导致该语言的真正破坏。因此,每个命令始终限制为1-3个参数。
我宁愿只使用一些词法分析器/解析器生成器组合,但是如果你想做你将要做的事情,我建议你至少首先用换行符拆分,然后用空格拆分,因此有明确的方法看它是否意味着给出2或3个论点。
即使您的语言是机器生成的,这很重要,如果您的生成器最终出现错误怎么办?早退,经常失败。
答案 6 :(得分:1)
你可以使用命令模式......你的每个动作都会知道它的id和操作,并在运行时将自己添加到一个列表中...然后你只需查找正确的命令,通过无论它需要什么背景,它都会执行操作。
答案 7 :(得分:1)
表驱动的方法似乎适合这一点,就像mxp所说的那样。如果你的函数有不同数量的参数,你可以在表中有一列指定同一行上函数的参数个数。