8

我想要您对以下伪代码的建议。请建议我如何改进它,是否可以使用一些设计模式。


// 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 个包含大量ifelse if 的代码屏幕有点难看。

4

8 回答 8

14

每个 ID 都有一个实现公共接口的类。基本上是策略模式IIRC。

所以你会调用(伪)代码,如:

StrategyFactory.GetStrategy(tokens[0]).parse(tokens[1..n])

于 2008-10-27T08:43:54.993 回答
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'

这很容易转化为 Spirit 解析器。每个生产规则都将变成单行,每个结束符号都将转换为函数。

#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;
}
于 2008-10-27T11:09:11.940 回答
4

您可以查看“表驱动方法”(如“代码完成”,第 2 版,第 18 章中所述)。我想这就是Cheery 所描述的。 这样做的好处是易于扩展。您只需向表中添加一些条目。该表可以是硬编码的,甚至可以在运行时加载。

Epaga 的建议类似,您也可以尝试通过多态性来解决这个问题,让专门的类针对不同的情况执行操作。这里的缺点是您必须编写新类以防发生更改。

于 2008-10-27T09:00:57.410 回答
2

您希望将其拆分为多个函数,每个 ID 一个,每个操作一个。

我通常使用的准则是屏幕高度。如果我的屏幕不能完全适应我的功能,我会开始考虑拆分。这样你就不需要滚动来查看函数的去向。正如我所说,这是一个指导方针,而不是规则,但我发现控制结构更实际。

如果您随后想采用 OO 方法并将其变成一堆类,如果您看到优势,欢迎您这样做。不过,请注意随之而来的所有管道。您可能希望保持简单。

戴夫

于 2008-10-27T08:51:55.307 回答
2

我已经看到了一个很好的解决这个问题的方法:函数的哈希表。

在编译时,会为每个支持的操作创建一个完美哈希函数,并且该操作与要调用的函数相关联(函数指针是哈希中的值,命令字符串是键)。

在运行时,通过使用命令字符串调用命令功能以在哈希表中查找函数。然后调用该函数,通过引用传递“数据”字符串。然后每个命令函数根据其规则解析出剩余的字符串......策略模式也适用于这一点。

使代码像状态机一样工作,这是(恕我直言)处理网络代码的最简单方法。

于 2008-10-27T16:20:17.867 回答
1

创建功能图。然后你会有这样的代码:

consumed_count = token_mapper[tokens[0]](tokens)
remove amount of consumed tokens according to the return value and repeat.

不过,无论如何我都不理解您的方法,您将编写一种难以处理且不灵活的语言。想一想:论点数量的微小差异会对该语言造成真正的破坏。因此,每个命令始终限制为 1-3 个参数。

我宁愿只使用一些词法分析器/解析器生成器组合,但如果你想做你要做的事情,我建议你至少先用换行符分割,然后用空格分割,因此有明确的方法来查看是否它旨在提供 2 或 3 个论点。

即使您的语言是机器生成的,这也很重要,但如果您的生成器最终出现错误怎么办?早失败,经常失败。

于 2008-10-27T08:53:01.263 回答
1

您可以使用命令模式……您的每个操作都会知道它的 ID 和操作,并在运行时将自己添加到列表中……然后您只需查找正确的命令,将其传递给任何上下文它需要并且它将执行操作。

于 2008-10-27T09:06:38.257 回答
1

表格驱动的方法似乎适合这个,就像 mxp 说的那样。如果您的函数有不同数量的参数,您可以在表中有一列指定同一行上函数的参数数量。

于 2008-10-27T09:08:27.160 回答