这种反模式/代码异味有一个名称吗?

19

首先,我要说的是,我不赞成这种做法,但最近我看到了这种做法,我想知道是否有一个名称可以用来指向罪魁祸首。接下来就是内容。

现在你有一个方法,并且你想返回一个值。你还想返回一个错误代码。当然,异常是更好的选择,但由于某些原因,你想使用错误代码。记住,我在这里只是扮演魔鬼的代言人。所以你创建了一个通用类,像这样:

class FunctionResult<T>
{
    public T payload;
    public int result;
}

然后像这样声明您的函数:

FunctionResult<string> MyFunction()
{
    FunctionResult<string> result;
    //...

    return result;
}

这种模式的一个变体是使用枚举而不是字符串来表示错误代码。现在,回到我的问题:这种方法有名称吗?如果有,是什么?


1
“当有人看到这个时,那个不是白痴的人会用锤子打断我的手指”模式。但这几乎适用于所有反模式。 - user1228
1
如果这是一种反模式,那么对于不直接支持异常的语言(我想到的是VB/VBA),推荐使用什么? - Onorio Catenacci
1
如果它们不支持异常,那么它们可能也不支持模板/泛型。 - Joel Coehoorn
Joel--是的,我没有想到。 :-) 我想我倾向于将模式(和反模式)视为与语言无关的,但你是对的;考虑到上下文,我可以看出那看起来像是一个反模式。 - Onorio Catenacci
13个回答

17
我同意这不是一个具体的反模式。根据使用情况,它可能是一种“异味”。有时候,你确实不想使用异常(比如返回的错误并不是“异常”的),但仍需要让服务返回一个包含错误和正确值的公共模型。这个模型可以被低级别的服务交互包装成异常或其他错误结构,但在服务层级上,它可以让服务返回结果和状态码,而无需定义一些可能需要跨远程边界翻译的异常结构。这段代码也不一定是错误的:考虑HTTP响应,其中包括很多不同的数据,包括状态码以及响应的正文。

我刚刚发现了代码异味,我会给你区分异味和反模式。不过我不确定如何更新问题以反映这一点。 - Joel Coehoorn

13

我在考虑将错误代码整合到一个模板中,而不是一般性地处理错误代码。 - Joel Coehoorn

10

这并不是反模式。C++标准库利用了这个特性,而.NET甚至在.NET框架中提供了一个特殊的FunctionResult类。它被称为Nullable。是的,这不仅限于函数返回值,但可以用于这种情况,而且实际上非常有用。如果.NET 1.0已经有了Nullable类,它肯定会被用于NumberType.TryParse方法,而不是out参数。


6

我通常将有效载荷作为(非const)引用传递,并将错误代码作为返回值。

我是一名游戏开发者,我们不使用异常。


5

Konrad说得没错,C#一直在使用双重返回值。但我有点喜欢C#中的TryParse、Dictionary.TryGetValue等方法。

int value;
if (int.TryParse("123", out value)) {
    // use value
}

替代

int? value = int.TryParse("123");
if (value != null) {
    // use value
}

大多数情况下,Nullable模式不适用于非值返回类型(例如类实例),这使得Dictionary.TryGetValue()无法使用。而TryGetValue比KeyNotFoundException更好(在调试器中没有“首次机会异常”,可能更有效),比Java的get()返回null的做法更好(如果预期为空值,则怎么办),也比先调用ContainsKey()更有效。但是这仍然有点混乱--因为这看起来像C#,所以应该使用out参数。通过实例化类,所有效率收益可能都会丢失。(除了“string”类型小写之外,这也可能是Java。在Java中,当然必须使用类来模拟双重返回值。)

值类型已经有了null值。Nullable基本上是为了在值类型中复制这个功能。当然,一旦null是一个可接受的值,这种方法就会失败。 - Konrad Rudolph

4

我不确定这是否是反模式。出于性能原因或者为了更明确地表明方法可能失败的事实,我通常看到人们使用它来代替异常处理。对我来说,这似乎是个人偏好而不是反模式。


3

我同意那些认为这不是反模式的说法。在某些情况下,这是一种完全有效的模式。异常是用于非常规情况的,返回值(如您的示例)应在预期情况下使用。有些领域希望从类中得到有效和无效的结果,而这两种结果都不应被建模为异常。

例如,给定 X 单位的汽油,汽车能否从 A 点到达 B 点,如果可以,还剩多少汽油?这种问题非常适合使用您提供的数据结构。不能从 A 点到达 B 点是可以预料的,因此不应使用异常。


2
这种方法实际上比我见过的其他一些方法要好得多。例如,C语言中的一些函数在遇到错误时会返回并似乎成功了。唯一能知道它们失败的方法是调用一个函数来获取最新的错误信息。
在我的MacBook上,我花了几个小时尝试调试信号量代码,最终才发现sem_init在OSX上不起作用!它编译没有错误,运行也没有引起任何错误 - 但信号量却不起作用,我无法找出原因。我同情那些将使用POSIX信号量的应用程序移植到OSX的人,他们必须处理已经被调试过的资源争用问题。

1
如果你期望你的方法偶尔失败,但不认为这是异常情况,我更喜欢在.NET Framework中使用的这种模式:
bool TryMyFunction(out FunctionResult result){    

     //...    
     result = new FunctionResult();
}

1
关于代码气味和反模式的争论让我想起了《幸存者》电视节目,其中各种编程结构试图将对方投票淘汰。我更喜欢看到“构造X有这样那样的优缺点”,而不是一个不断演变的规则清单,告诉我们应该做什么,不应该做什么。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接