特定函数与许多参数与上下文相关

一个例子

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

具体案例实现(function太多)

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。

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

我可能也会避免第二种选择。 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 } ... } 

编辑:回答格雷格S.

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

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

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

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

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

我更喜欢论证方式。

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

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

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

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

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

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

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

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

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

 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"); 

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

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

 if(foo) writeInUpperCaseAndCentered("Foobar!"); else if(bar) writeInLowerCaseAndCentered("Foobar!"); else ... 

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

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