具体功能与许多参数与上下文相关

时间:2010-07-01 13:58:34

标签: c function arguments global-variables

示例

假设我们有一个要写的文本,可以转换为“大写或小写”,并且可以“左,中,右”打印。

具体案例实施(功能太多)

writeInUpperCaseAndCentered(char *str){//..}
writeInLowerCaseAndCentered(char *str){//..}
writeInUpperCaseAndLeft(char *str){//..}
and so on...

VS

许多Argument函数(糟糕的可读性,甚至在没有很好的自动完成IDE的情况下很难编码)

write( char *str , int toUpper, int centered ){//..}

VS

依赖于上下文(难以重用,难以编码,使用丑陋的全局变量,有时甚至无法“检测”上下文)

writeComplex (char *str)
{    
    // analize str and perhaps some global variables and 
    // (under who knows what rules) put it center/left/right and upper/lowercase
}

也许还有其他选择..(并且欢迎)

问题是:

对于这种(经常性)三难问题,是否有良好做法经验/学术建议

编辑:

我通常做的是结合“特定情况”实现,内部(我的意思是不在标题中)一般常见的多参数函数,只实现用例,隐藏丑陋的代码,但我不知道如果有更好的方式,我不知道。这种事让我意识到OOP的发明原因。

6 个答案:

答案 0 :(得分:7)

我会避免你的第一个选项,因为正如你所说,你最终必须实现的功能数量(尽管可能仅作为宏)会失控。当您决定添加斜体支持时,计数会翻倍,并且再次加倍下划线。

我可能也会避免第二种选择。 Againg会考虑当您发现有必要添加斜体或下划线支持时会发生什么。现在,您需要向函数添加另一个参数,找到调用函数的所有情况并更新这些调用。简而言之,令人讨厌,尽管你可以通过适当使用宏来简化这个过程。

这留下了第三种选择。实际上,使用bitflags可以获得其他替代方案的一些好处。例如

#define WRITE_FORMAT_LEFT   1
#define WRITE_FORMAT_RIGHT  2
#define WRITE_FORMAT_CENTER 4
#define WRITE_FORMAT_BOLD   8
#define WRITE_FORMAT_ITALIC 16
....
write(char *string, unsigned int format)
{
  if (format & WRITE_FORMAT_LEFT)
  {
     // write left
  }

  ...
}

编辑:回答Greg S。

我认为最大的改进是,这意味着如果我决定在此时添加对带下划线的文本的支持,我需要两个步骤

  1. #define WRITE_FORMAT_UNDERLINE 32添加到标题
  2. write()
  3. 中添加对下划线的支持

    此时它可以调用write(...,... | WRITE_FORMAT_UNLDERINE)。更重要的是,我不需要修改预先存在的写入调用,如果我在其签名中添加了一个参数,我将不得不这样做。

    另一个潜在的好处是,它允许您执行以下操作:

    #define WRITE_ALERT_FORMAT  (WRITE_FORMAT_CENTER | \
                                 WRITE_FORMAT_BOLD |   \
                                 WRITE_FORMAT_ITALIC)
    

答案 1 :(得分:3)

我更喜欢论证方式。

因为会有一些代码需要使用所有不同的场景。从每个场景中创建一个函数会产生代码重复,这很糟糕。

不使用每个不同情况的参数(toUpper,居中等等),而是使用结构。如果您需要添加更多案例,那么您只需要更改结构:

typedef struct {
    int toUpper;
    int centered;
    // etc...
} cases;
write( char *str , cases c ){//..}

答案 2 :(得分:2)

我会选择方法1和2的组合。

编写方法(A),其中包含您现在需要/可以想到的所有参数以及没有额外参数的“裸”版本(B)。此版本可以使用默认值调用第一个方法。如果您的语言支持,请添加默认参数。我还建议您为参数使用有意义的名称,并在可能的情况下使用枚举而不是幻数或一系列true / false标志。这将使您更容易阅读代码以及实际传递的值,而无需查找方法定义。

这为您提供了一组有限的维护方法,90%的用法将是基本方法。

如果您需要扩展功能,请稍后使用新参数添加新方法并修改(A)以调用此方法。您可能希望修改(B)以调用它,但这不是必需的。

答案 3 :(得分:1)

我已经多次遇到过这种情况 - 我的偏好不是上述情况,而是使用单个格式化程序对象。我可以为它提供指定特定格式所需的参数数量。

这样做的一个主要优点是我可以创建指定逻辑格式而不是物理格式的对象。例如,这允许:

Format title = {upper_case, centered, bold};
Format body = {lower_case, left, normal};

write(title, "This is the title");
write(body, "This is some plain text");

将逻辑格式与物理格式分离,可以提供与样式表大致相同的功能。如果您想将所有标题从斜体更改为粗体,请将您的体型从左对齐更改为完全对齐等,这样做变得相对容易。使用当前代码,您可能最终会搜索所有代码并“手动”检查以确定特定的小写左对齐项是否是要重新格式化的正文,或者你要留下一个脚注......

答案 4 :(得分:0)

正如您已经提到的,一个引人注目的要点是可读性:writeInUpperCaseAndCentered("Foobar!")write("Foobar!", true, true)更容易理解,尽管您可以通过使用枚举消除该问题。另一方面,有参数避免了像:

这样的尴尬结构
if(foo)
  writeInUpperCaseAndCentered("Foobar!");
else if(bar)
  writeInLowerCaseAndCentered("Foobar!");
else
 ...

以我的拙见,这是一个非常强烈的争论(没有双关语意图)的争论方式。

答案 5 :(得分:0)

我建议使用更多内聚函数,而不是可以执行各种操作的超级函数,除非真正需要超级函数(如果一次只打印一种类型,printf会非常笨拙)。通常不应将签名冗余视为冗余代码。从技术上讲,它是更多代码,但您应该更专注于消除代码中的逻辑冗余。结果是使用非常简洁,定义明确的行为更容易维护的代码。当编写/使用多个函数似乎是多余的时,可以将其视为理想选择。