避免过多的函数参数:以类为中心或以函数为中心的方法?

Fra*_*ank 6 c++ parameters class-design function parameter-passing

你如何修复以下传递过多参数的错误代码?

void helper1(int p1, int p3, int p5, int p7, int p9, int p10) {
  // ...
}

void helper2(int p1, int p2, int p3, int p5, int p6, int p7, int p9, int p10) {
  // ...
}

void foo(int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8, 
         int p9, int p10) {
  helper1(p1, p3, p5, p7, p9, p10);
  helper2(p1, p2, p3, p5, p6, p7, p9, p10);
}
Run Code Online (Sandbox Code Playgroud)

我看到两种不同的方法:

方法1:将所有函数放在一个类中

class Foo {
  private:
    int p1, p2, p3, p4, p5, p6, p7, p8, p9, p10;
    void helper1() {}
    void helper2() {}
  public:
    void foo() {
      helper1();
      helper2();
    }
    // add constructor
};
Run Code Online (Sandbox Code Playgroud)

方法2:只需将参数作为一个类传递

struct FooOptions {
    int p1, p2, p3, p4, p5, p6, p7, p8, p9, p10;
};

void helper1(const FooOptions& opt) {
  // ...
}

void helper2(const FooOptions& opt) {
  // ...
}

void foo(const FooOptions& opt) {
  helper1(opt);
  helper2(opt);
}
Run Code Online (Sandbox Code Playgroud)

这些方法有哪些优点和缺点?

方法1的一个优点是 - 如果您将helper函数设置为虚拟 - 您可以对它们进行子类化和重载,从而增加灵活性.但是,在我的情况下(在我给出的玩具迷你示例之外),这样的助手通常是模板化的,所以无论如何它们都不能是虚拟的.

方法2的一个优点是辅助函数也可以很容易地从其他函数调用.

(这个问题是相关的,但没有讨论这两个选择.)

whe*_*ies 7

简答:

新年快乐!我会避免选项#1,只有选项#2,如果参数可以分为清晰和逻辑组,这些组与你的功能有关.

答案很长

我已经看到很多函数的例子,就像你从同事那里描述的那样.我会同意你的看法,这是一个糟糕的代码味道.但是,将参数分组到类中只是为了不必传递参数并相当任意地决定根据这些辅助函数对它们进行分组会导致更难闻的气味.你必须问问自己,你是否提高了对你之后的可读性和理解力.

calcTime(int p1, int p2, int p3, int p4, int p5, int p6) {
    dist = calcDistance( p1, p2, p3 );
    speed = calcSpeed( p4, p5, p6 );
    return speed == 0 : 0 ? dist/speed; }
Run Code Online (Sandbox Code Playgroud)

在那里,您可以将事物分组以使其更容易理解,因为参数之间存在明显的区别.然后我会建议方法#2.

另一方面,我经常使用的代码如下:

calcTime(int p1, int p2, int p3, int p4, int p5, int p6) {
    height = height( p1, p2, p3, p6 );
    type = getType( p1, p4, p5, p6 );
    if( type == 4 ) {
        return 2.345; //some magic number
    }
    value = calcValue( p2, p3, type ); //what a nice variable name...
    a = resetA( p3, height, value );
    return a * value; }
Run Code Online (Sandbox Code Playgroud)

这让你觉得这些参数对于分类到有意义的分类方面并不完全友好.相反,你可以更好地摆脱周围的事情

calcTime(Type type, int height, int value, int p2, int p3)
Run Code Online (Sandbox Code Playgroud)

然后调用它

calcTime(
    getType( p1, p4, p5, p6 ),
    height( p1, p2, p3, p6 ),
    p3,
    p4 );
Run Code Online (Sandbox Code Playgroud)

当你头脑里的小声音尖叫"干,干,干!"时,可能会让你的脊椎发抖.哪一个更具可读性,因此可维护?

选项#1在我脑海中是不可取的,因为很可能有人会忘记设置其中一个参数.这很容易导致难以检测到通过简单单元测试的错误.因人而异.