要为“ false”的布尔函数参数添加正确的注释?


19

从一些开源项目中,我收集了以下编码样式

void someFunction(bool forget);

void ourFunction() {
  someFunction(false /* forget */);
}    

我一直对false这里的含义感到怀疑。它的意思是“忘记”,还是“忘记”是指它的相应参数(如上述情况),而“假”是要否定它?

哪种风格最常用,什么是避免歧义的最佳方法(或某些更好的方法)?


38
使用枚举(即使只有2个选项)而不是
布尔值

21
某些语言支持命名参数。在这种语言中,您可以使用someFunction(forget: true);
Brian

3
我觉得有义务在Flag Arguments上提供Martin Fowler的论点(也讨论布尔设置器)。通常,请尝试避免它们。
FGreg 2013年

3
显而易见,可以撒谎。因此,始终是更好地使你的代码自我记录,因为有些人会改变truefalse不更新自己的评论。如果您无法更改API,那么对此进行评论的最佳方法是someFunction( false /* true=forget, false=remember */)
Mark Lakata

1
@Bakuriu-实际上,我可能仍然希望公共API具有两个单独的方法(sortAscendingsortDescending,或类似方法)。现在,在内部,它们都可以调用相同的私有方法,该私有方法可能具有这种参数。实际上,如果语言支持的话,可能我要传递的是包含排序方向的lambda函数……
Clockwork-Muse

Answers:


33

在您发布的示例代码中,它看起来像是forget一个标志参数。(我不能确定,因为函数纯粹是假设的。)

标志参数是代码的味道。它们表明一个功能不仅仅可以做一件事,而一个好的功能应该只能做一件事。

为避免使用flag参数,请将函数分解为两个函数,以说明函数名称的差异。

标记参数

serveIceCream(bool lowFat)

无标志参数

serveTraditionalIceCream()
serveLowFatIceCream()

编辑:理想情况下,您根本不需要保留带有flag参数的函数。在Fowler所说的纠结的实现中,有些情况是完全分开的功能会创建重复的代码。但是,参数化函数的圈复杂度越高,则摆脱它的论据就越强。


这只是预感,但是一个名为的参数forget听起来像功能嫉妒。呼叫者为什么要告诉另一个对象忘记某些东西?可能存在更大的设计问题。


4
+17,戴上头盔。什么做的身体serveTraditionalIceCreamserveLowFatIceCream样子?我有一个列举14种冰淇淋的枚举。
JohnMark13 2013年

13
对于公共方法而言,此约定是好的,但是正如JohnMark所暗示的那样,SRP的另一半是“好的方法应该是唯一可以完成其工作的方法”。我看到这种方法有N + 1个变体。N个没有参数的public,所有这些都使用您要避免公开的参数调用一个私有方法。在某个时候,您只是放弃并公开该死的参数。代码气味并不一定意味着应该重构代码。它们只是需要在代码审查中重新审视的东西。
KeithS

@Aaron通过“功能嫉妒”意味着什么?
极客

27

用外行的话来说:

  • false 是文字。
  • 你正在传递文字 false
  • 你告诉someFunction不要忘记
  • 您正在告诉someFunction参数忘记是false
  • 你是告诉someFunction要记住

我认为如果函数像这样会更好:

void someFunction(bool remember);

你可以叫它

void ourFunction() {
  someFunction(true);
} 

或保留旧名称,但将包装函数更改为

void ourFunctionWithRemember() {
  someFunction(false);
} 

编辑:

如@Vorac所述,请始终努力使用正面词。双重否定令人困惑。


15
为该想法+1,始终努力使用正面的单词。双重否定令人困惑。
Vorac 2013年

1
同意,好主意。告诉系统不要做某事是令人困惑的。如果接受布尔型参数,请明确表示。
布兰登

在大多数情况下,我认为您也想比更加具体remember,除非函数名称的含义remember 非常明显。 rememberToCleanUp* or *persist或者其他的东西。
itsbruce

14

该参数可以很好地命名;在不知道函数名称的情况下很难分辨。我认为有人评论写的函数的原作者,并且它是通过什么样的提醒falsesomeFunction手段,但没有人来一起之后,它乍一看有点不清楚。

使用正变量名(在Code Complete中建议)可能是最简单的更改,它会使此代码段更易于阅读,例如

void someFunction(boolean remember);

然后ourFunction变成:

void ourFunction() {
    someFunction(true /* remember */);
}

但是,使用枚举会使函数调用更容易理解,但会牺牲一些支持代码:

public enum RememberFoo {
    REMEMBER,
    FORGET
}

...

void someFunction(RememberFoo remember);

...

void ourFunction() {
    someFunction(RememberFoo.REMEMBER);
}

如果您someFunction由于某种原因无法更改签名,则使用临时变量也会使代码更易于阅读,就像通过无其他原因引入变量来简化条件,除了使人类更容易解析代码一样。

void someFunction(boolean remember);

...

void ourFunction() {
    boolean remember = false;
    someFunction(remember);
}

1
设为remembertrue意味着忘记(在您的示例中someFunction(true /* forget */);)?
Brian

2
enum到目前为止,这是最好的解决方案。仅仅因为类型可以表示为-即bool它们是同构的-并不意味着应该这样表示。相同的论点适用于string甚至int
乔恩·普迪

10

重命名该变量,以便布尔值有意义。

这比添加注释来解释函数的参数要好一百万倍,因为名称不明确。


3
那没有回答问题。当在相距4行的同一个文件中定义并调用该方法时,所有事情都是显而易见的。但是,如果您现在看到的是来电者,该怎么办?如果它有多个布尔值怎么办?有时,简单的在线注释会大有帮助。
布兰登

@Brandon这不是反对调用布尔doNotPersist(或者更好的是Persist)的论点。坦率地说,不说要忘记什么就称其为“忘记”。哦,一种需要几个布尔值作为选择的方法会发臭到高高的天堂。
itsbruce

5

使用更具描述性的名称创建一个本地布尔值,然后为其分配值。这样,其含义将更加清楚。

void ourFunction() {
    bool takeAction = false;  /* false means to forget */
    someFunction( takeAction );
}    

如果您不能重命名该变量,则注释应更具表现力:

void ourFunction() {
    /* false means that the method should forget what was requested */
    someFunction( false );
}    

1
绝对是很好的建议,但是我认为它不能解决/* forget */注释必须解决的问题,即如果没有在您面前的函数声明,可能很难记住要设置的内容false。(这就是为什么我认为@Esailija的建议添加一个枚举更好,以及为什么我喜欢允许使用命名参数的语言。)
弄乱了机器人

@StevenBurnap-谢谢!您说对了,因为我的旧答案在解决OP的问题时还不够清楚。我对其进行了编辑以使其更加清晰。


2

这是一个奇怪的评论。

从编译器的角度来看,someFunction(false /* forget */);实际上是someFunction(false);(注释已删除)。因此,该行所做的只是someFunction在第一个(也是唯一一个)参数设置为的情况下进行调用false

/* forget */只是参数的名称。它可能只是快速(肮脏的)提醒而已,实际上并不需要在那里。只需使用不太模糊的参数名称,就根本不需要注释。


1

Clean代码的建议之一是最大程度地减少不必要的注释1的数量(因为它们容易腐烂),并正确命名函数和方法。

接下来,我将删除评论。毕竟,当您将鼠标放在函数上时,现代的IDE(例如eclipse)会弹出一个带有代码的框。看到代码应该清除歧义。


1评论一些复杂的代码是可以的。


btw谁说过这样的话:“程序员最糟糕的问题是如何命名变量,并以一个偏移量命名”?
2013年

4
您可能正在寻找martinfowler.com/bliki/TwoHardThings.html作为其来源。我听说过一些调整,“计算机科学中只有两件难事:缓存无效,命名和一次出错。”

1

显而易见,可以撒谎。因此,最好使代码自行记录而无需诉诸注释来解释,因为某些人(也许您)会更改truefalse注释而不更新注释。

如果您无法更改API,那么我要使用2个选项

  • 更改注释,使其始终为true,无论代码如何。如果只调用一次,这是一个很好的解决方案,因为它将文档保持在本地。
     someFunction(false / * true = forget,false = remember * /);`
  • 使用#defines,特别是如果您多次调用它。
     #定义忘记真
     #define REMEMBER false
     someFunction(REMEMBER);

1

我喜欢关于使注释始终为true的答案,但是虽然不错,但我认为它忽略了此代码的根本问题-使用文字来调用它。

调用方法时应避免使用文字。局部变量,可选参数,命名参数,枚举-如何最好地避免使用它们取决于语言和可用的内容,但是请尽量避免使用它们。文字有价值,但没有意义。


-1

在C#中,我使用命名参数使其更加清晰

someFunction(forget: false);

enum

enum Memory { Remember, Forget };

someFunction(Memory.Forget);

或超载:

someFunctionForget();

或多态`

var foo = new Elephantine();

foo.someFunction();

-2

命名应始终解决布尔值的歧义。我总是将布尔值命名为“ isThis”或“ shouldDoThat”,例如:

void printTree(Tree tree, bool shouldPrintPretty){ ... }

等等。但是,当您引用别人的代码时,最好在传递值时留下注释。


这如何回答所提问题?
gnat 2013年

@gnat我在回答他有关使用布尔参数解决歧义的问题。也许我看错了他的问题。
dchhetri
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.