我怎么能改进这个C ++代码

时间:2008-10-27 08:41:45

标签: c++ design-patterns refactoring

我希望您对以下伪代码提出建议。请建议我如何改进它,无论我是否可以使用一些设计模式。


// 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

8 个答案:

答案 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所说的那样。如果你的函数有不同数量的参数,你可以在表中有一列指定同一行上函数的参数个数。