如何命名既执行任务又返回布尔值作为状态的方法?


33

如果有办法

bool DoStuff() {
    try {
        // doing stuff...
        return true;
    }
    catch (SomeSpecificException ex) {
        return false;
    }
}

它应该被称为IsStuffDone()吗?

用户可能会误解这两个名称:如果名称是DoStuff()为什么它返回布尔值?如果名称IsStuffDone()不明确,则该方法是执行任务还是仅检查其结果。

这种情况有约定吗?还是一种替代方法,因为这被认为是有缺陷的?例如,在具有输出参数的语言(如C#)中,可以将布尔状态变量作为1传递给方法,而方法的返回类型为void

编辑:在我的特定问题中,异常处理不能直接委托给调用者,因为该方法是接口实现的一部分。因此,调用者不必负责处理不同实现的所有异常。它不熟悉那些例外。但是,调用者可以处理自定义异常,如npinti的answer和comment中StuffHasNotBeenDoneForSomeReasonException建议的那样


2
这取决于功能的使用和正在完成的工作。如果有必要,这些东西需要正确执行,那么我认为这种方法是有缺陷的,因为该函数的用户可能会错过布尔标志,并且也缺少由异常提供的信息。
Benni 2014年

7
在大多数情况下,我将其称为“中断”,因为返回a boolean而不是包装或传递异常几乎总是错误的。
maaartinus 2014年

2
这种异常处理程序很少是一个好主意。仅捕获您期望的特定异常,而不是全部。并且,如果可能的话,请避免将其扔在第一位,从而消除了捕获它的必要。
CodesInChaos

2
嗯... BadlyDesignedMethodInSeriousNeedOfRefactoring?并回答您有关异常的问题-我要么让调用者处理它们,要么捕获它们,然后引发一个自定义异常,该异常意味着“此方法不起作用”。分享并享受。
Bob Jarvis-恢复莫妮卡2014年

5
对于所有说的人:仅仅抛出(或让它通过)一个异常,就对如何使用此代码做出了毫无根据的假设。一种可能的情况是存在一个要解决的问题,它采用了各种启发式解决方法,它们以越来越高的成本解决了问题的更大子类。写这样的东西是有道理的if (FirstMethodSucceeds(problem) or SecondMethodSucceeds(problem) or ...) Hurray(); else UniversalSolve(problem);。对(自定义?)异常执行相同操作将变得毫无用处,变得更加复杂。
Marc van Leeuwen 2014年

Answers:


68

在.NET中,您经常会有成对的方法,其中的一个可能会引发异常(DoStuff),另一个会返回布尔状态,并在成功执行后通过out参数(TryDoStuff)返回实际结果。

(Microsoft将其称为“ Try-Parse模式”,因为它最突出的示例TryParse就是各种原始类型的方法。)

如果Try前缀在您的语言中不常见,那么您可能不应该使用它。


3
+1这就是我在其他地方看到这种事情的方式。if (TryDoStuff()) print("Did stuff"); else print("Could not do stuff");我认为这是一个非常标准且直观的习惯用法。
Karl Nicoll 2014年

12
应该注意的是,TryDoStuff方法被假定为彻底失败,并且在返回false时没有副作用。
Trillian 2014年

仅供参考,Remove.NET框架中还提供了一些方法,例如,这些方法将从数据结构(例如Dictionary)中删除元素,然后返回bool。API的使用者可以决定使用它还是忽略它。但是,错误被报告为异常。
Omer Iqbal 2014年

18

如果您只是将异常抛出给调用代码怎么办?

这样,您就将异常处理委托给谁在使用代码。如果将来您想执行以下操作怎么办:

  • 如果没有引发异常,请执行操作A
  • 如果(例如)FileNotFoundException抛出a,请执行操作B
  • 如果引发任何其他异常,请执行操作C

如果您抛出异常,则上述更改将需要添加一个额外的catch块。如果保持原样,则需要更改方法以及调用该方法的位置,具体取决于项目的复杂性,该位置可以位于多个位置。


1
如果异常是无法委托的,应该在内部处理,该怎么办?
Limbo Exile 2014年

2
@LimboExile:我认为您仍然可以在内部处理它,然后可能引发另一个异常,也许是您自己的异常。这说明发生了不应该发生的事情,但是与此同时,您没有揭露真正的内幕,我认为这就是为什么要在内部处理异常。
npinti 2014年

6
可能值得牢记的是,异常情况下应该抛出异常。如果DoStuff失败是常见或正常的,则针对失败案例抛出异常将类似于控制程序流并带有异常,这是不好的。异常也具有固有的性能成本。如果DoStuff失败是由于错误导致的罕见情况,那么按照@npinti的建议,异常肯定是解决方法。
Karl Nicoll 2014年

1
@KarlNicoll是否某些东西是“例外”是完全主观的。主要标准应该是,如果没有人对错误进行任何操作,是否要使程序崩溃,以及是否希望函数的类型中出现错误的可能性。此外,例外并非代价高昂的事实;这取决于语言以及如何扔掉它们。在Python中,异常很便宜,如果您剥离堆栈跟踪信息,在Java中,异常也很便宜。即使存在性能成本,在进行概要分析之前也要过早优化它。
2014年

1
@Doval-我同意这是主观的,这就是为什么OP不应完全否定npinti的答案,因为这很可能是最好的做法。我的观点是抛出异常并不总是最好的方法。在很多情况下,失败并不能证明抛出异常并可能导致应用程序崩溃。例如,如果DoStuff()内部代码引发异常后OP的方法清除,并且该方法的Post-condition仍然正确,则返回代码可能是更好的选择,因为已处理了错误。
Karl Nicoll 2014年

7

DoStuff() 就足够了,应该记录函数的返回值,而无需在函数名称中提及它,而是寻找许多可用的API:

的PHP

// this method update and return the number affected rows 
// called update instead of updateAndGetAffectedCount
$affected = $query->update(/* values */);

锋利的C

// Remove item from the List
// returns true if item is successfully removed; otherwise, false.
public bool Remove( T item )

6

在Java中,Collection API定义了一个返回布尔值的add方法。它基本上返回添加是否更改了集合。因此,对于,List它通常会返回true,因为已添加了该项目;而对于了,Set它可能会返回false(如果该项目已经存在),因为Sets最多允许每个唯一的项目一次。

也就是说,如果添加了集合不允许的项目(例如,空值),则添加操作将抛出a NullPointerException而不是返回false。

当对案件应用相同的逻辑时,为什么需要返回布尔值?如果要隐藏异常,请不要。只是抛出异常。这样,您可以查看出了什么问题。如果确实需要布尔值(由于可能未完成),则出于异常以外的其他原因,请将该方法命名为DoStuff(),因为它表示行为。它的东西。


1

可能由于各种原因(异常,正常情况)而失败,因此可以使用以下方法:

boolean doStuff() - returns true when successful

重要的是要记录布尔值的含义。


1

我通常有方法返回一个OperationResult(或OperationResult<T>)并IsSuccessOperationResult适当的位置设置属性。如果“常规”方法无效,则返回OperationResult;如果“常规”方法返回对象,则返回OperationResult<T>ItemOperationResult适当的位置设置属性。我也建议其上的方法OperationResult,如.Failed(string reason).Succeeded(T item)OperationResult很快就成为您系统中的一种常见类型,开发人员开始了解如何处理它。


0

这实际上取决于您使用的语言。就像某些语言一样,在许多语言中或根本没有任何语言中确实存在约定和协议。

例如,在使用Objective-C语言和iOS / Mac OS X SDK的方法名称通常遵循以下原则:

- (returndatatype) viewDidLoad {
- (returndatatype) isVisible {
- (returndatatype) appendMessage:datatype variablename with:datatype variablename {

如您所见,那里有一个叫做viewDidLoad的方法。我更喜欢在创建自己的应用程序时使用这种类型的命名。这可以用来检查是否如您所说发生了某些事情。我相信您对方法的名称思考得太深了。大多数方法无论执行何种操作都会返回其状态,例如:

在PHP中,如果连接失败,mysql_connect()将返回false。它没有说会,但是有,并且文档说有,所以不会被程序员误解。


嗯,但是viewDidLoad更像是一个回调通知。用户不调用它来加载视图。而是在加载视图后调用它。同样:isVisible不会设置可见性,而只是返回当前值。它什么也没
lilbyrdie 2014年

0

我想建议使用“ Try”前缀。对于该方法可能成功或失败的情况,这是一种众所周知的模式。Microsoft在.NET Framework中已经使用了这种命名模式(例如TryParseInt)。

但是,这可能与命名无关。它与如何设计方法的输入/输出参数有关。

我的想法是,如果某个方法具有返回值,则调用该方法的目的应该是获取该返回值。因此,应避免使用返回类型指示操作状态。

在C#中,我更喜欢使用out参数。使用out我将参数标记为side参数。特别是C#6将支持内联变量声明。

DoStuff(out var isStuffDone); // The out parameter name clearly states its purpose.
DoStuff(out var _); // I use _ for naming parameters that I am ignoring.

0

我会根据方法的主要作用来选择一个名称。实际上,该方法返回布尔值并不强制使用“ Is”前缀。让我们看一些Java代码,它们相当广泛地使用'Is'前缀。看一下java.io.File.delete()。它返回boolean,但是没有被调用isFileDeleted()。原因是该方法的主要作用是删除文件。有人为了删除文件而调用此方法。

布尔值只是用来传达工作结果。另一方面,让我们看看java.util.Map.isEmpty()。显然,您调用了这种方法来找出该集合是否为空。您不希望任何事情完成。您只想了解当前状态。

因此,就我个人而言,我绝对会坚持DoStuff()

对我来说,这是一个非常重要的问题。很多时候,当我维护遗留代码时,我会偶然发现一种我个人所说的“说谎方法”。名称表示动作A,但主体表示动作B。最令人毛骨悚然的例子是使用getter方法更改数据库中的数据。

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.