这种反模式/代码气味有名称吗?
Posted
技术标签:
【中文标题】这种反模式/代码气味有名称吗?【英文标题】:Is there a name for this anti-pattern/code smell? 【发布时间】:2010-09-09 12:24:46 【问题描述】:首先让我说我不提倡这种方法,但我最近看到了它,我想知道是否有一个名称可以用来指出有罪的一方。就这样吧。
现在你有了一个方法,你想返回一个值。你也想要返回一个错误代码。当然,异常是一个更好的选择,但无论出于何种原因,您都需要一个错误代码。记住,我在这里扮演魔鬼的拥护者。所以你创建了一个泛型类,像这样:
class FunctionResult<T>
public T payload;
public int result;
然后像这样声明你的函数:
FunctionResult<string> MyFunction()
FunctionResult<string> result;
//...
return result;
此模式的一个变体是使用枚举作为错误代码而不是字符串。现在,回到我的问题:有这个名字吗?如果有,它是什么名字?
【问题讨论】:
“不是白痴的人看到这个会用锤子打断我的手指”的模式。但这几乎适用于所有反模式。 如果这是一种反模式,那么对于不直接支持异常的语言有什么建议(我在考虑 VB/VBA)。 如果他们不支持异常,他们可能也不支持模板/泛型。 乔尔——是的,没想到这一点。 :-) 我想我倾向于认为模式(和反模式)与语言无关,但你是对的;鉴于上下文,我可以看到这看起来像是一种反模式。 【参考方案1】:我同意这不是专门的反模式。根据使用情况,它可能是一种气味。人们实际上不想使用异常是有原因的(例如,对于初学者来说,返回的错误不是“异常”)。
在某些情况下,您希望服务返回其结果的通用模型,包括错误和良好值。这可能由将结果转换为异常或其他错误结构的低级别服务交互包装,但在服务级别,它允许服务返回结果和状态代码,而无需定义一些可能的异常结构必须跨越远程边界。
此代码也不一定是错误:考虑一个 HTTP 响应,它包含许多不同的数据,包括状态代码以及响应的正文。
【讨论】:
我刚刚发现了代码异味,我将授予您异味与反模式。不过,不确定如何更新问题以反映这一点。【参考方案2】:叫做“Replace Error Code with Exception”
【讨论】:
我更多的是考虑在模板中组合错误代码,而不是一般的错误代码。【参考方案3】:嗯,它不是反模式。 C++ 标准库利用了这个特性,.NET 甚至在 .NET 框架中提供了一个特殊的FunctionResult
类。它被称为Nullable
。是的,这不仅限于函数结果,但它可以用于这种情况,实际上在这里非常有用。如果 .NET 1.0 已经有了 Nullable
类,它肯定会用于 NumberType.TryParse
方法,而不是 out
参数。
【讨论】:
【参考方案4】:我通常将有效负载作为(非 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 的做法更好(如果预期 null 值怎么办),并且比不得不首先调用 ContainsKey()。
但是这个是还是有点麻烦——因为这看起来像C#,那么它应该使用一个out参数。实例化类可能会失去所有效率提升。
(可以是Java,除了“字符串”类型是小写的。在Java中你当然必须使用一个类来模拟双重返回值。)
【讨论】:
嗯,非值类型已经有null
值。 Nullable
基本上是为值类型复制这一点的努力。当然,只要null
是一个可接受的值,这个方法就会失败。【参考方案6】:
我不确定这是一个反模式。出于性能原因,或者可能使该方法可能失败的事实更加明确,我经常看到它被用来代替异常。对我来说,这似乎是个人偏好,而不是反模式。
【讨论】:
【参考方案7】:我同意那些说这不是反模式的人的观点。在某些情况下,它是一个完全有效的模式。 exception 情况下的例外情况,应在预期情况下使用返回值(如您的示例中所示)。一些域期望类的有效和无效结果,这些都不应该被建模为异常。
例如,给定 X 油量,汽车可以从 A 到 B,如果可以,还剩下多少油?这种问题非常适合您提供的数据结构。预计无法从 A 到 B,因此不应使用例外。
【讨论】:
【参考方案8】:这种方法实际上比我见过的其他方法要好得多。例如,C 中的某些函数在遇到错误时会返回并且似乎成功了。判断它们失败的唯一方法是调用将获取最新错误的函数。
在我终于发现 sem_init 在 OSX 上无法运行之前,我花了几个小时尝试在我的 MacBook 上调试信号量代码!它编译没有错误,运行也没有导致任何错误——但是信号量没有工作,我不知道为什么。我很同情那些将使用 POSIX semaphores 的应用程序移植到 OSX 并且必须处理已经调试的资源争用问题的人。
【讨论】:
【参考方案9】:“无法确定这是否是错误”模式怎么样。似乎如果您确实有异常但想返回部分结果,则将结果包装在异常中。
【讨论】:
【参考方案10】:如果您不想使用异常,最简洁的方法是让函数返回错误/成功代码,并采用一个引用或指针参数来填充结果。
我不会称之为反模式。这是一种经过充分验证的可行方法,通常比使用异常更可取。
【讨论】:
【参考方案11】:如果您希望您的方法偶尔会失败,但不认为这是异常情况,我更喜欢 .NET Framework 中使用的这种模式:
bool TryMyFunction(out FunctionResult result)
//...
result = new FunctionResult();
【讨论】:
【参考方案12】:关于气味和反模式的辩论让我想起了“幸存者”电视节目,在那里你有各种编程结构试图在岛上相互投票。我更愿意看到“构造 X 有这样那样的优点和缺点”,而不是不断发展的关于应该做什么和不应该做什么的法令列表。
【讨论】:
【参考方案13】:为了捍卫反模式的名称,这段代码适合以多种方式使用:
-
对象 x = MyFunction().payload; (忽略返回结果 - 非常糟糕)
int 代码 = MyFunction().result; (丢弃有效载荷 - 如果这是预期用途,可能没问题。)
函数结果 x = MyFunction(); //... (一堆额外的 FunctionResult 对象和额外的代码来检查它们)
如果您需要使用返回码,那很好。但是然后使用返回码。不要尝试在其中打包额外的有效负载。这就是 ref 和 out 参数 (C#) 的用途。可为空的类型可能是一个例外,但这只是因为语言中有额外的糖来支持它。
如果您仍然不同意此评估,请否决此答案(不是整个问题)。如果您确实认为这是一种反模式,那么请支持它。我们将使用这个答案来看看社区的想法。
【讨论】:
我更喜欢 TryParse 之类的东西,它是有效负载的一个输出参数,函数返回结果。以上是关于这种反模式/代码气味有名称吗?的主要内容,如果未能解决你的问题,请参考以下文章