我写了一个表示纬度/经度坐标的结构。对于纬度,其值的范围是-180至180,对于纬度,其值的范围是90至-90。
如果该结构的用户给我提供的值超出该范围,则我有2个选择:
- 引发异常(arg超出范围)
- 将值转换为约束
因为坐标-185具有含义(因为极坐标可以很容易地将其转换为+175),所以我可以接受并转换它。
最好抛出一个异常来告诉用户他的代码给了我一个它不应该具有的值吗?
编辑:另外,我知道纬度/经度和坐标之间的区别,但是我想简化一下以便于讨论-这并不是最聪明的主意
我写了一个表示纬度/经度坐标的结构。对于纬度,其值的范围是-180至180,对于纬度,其值的范围是90至-90。
如果该结构的用户给我提供的值超出该范围,则我有2个选择:
因为坐标-185具有含义(因为极坐标可以很容易地将其转换为+175),所以我可以接受并转换它。
最好抛出一个异常来告诉用户他的代码给了我一个它不应该具有的值吗?
编辑:另外,我知道纬度/经度和坐标之间的区别,但是我想简化一下以便于讨论-这并不是最聪明的主意
Answers:
如果您的问题的核心是这个...
如果某些客户端代码传递了一个参数,该参数的值对于我的数据结构正在建模的事物无效,那么我应该拒绝该值还是将其转换为明智的方法?
...然后我的一般回答是“拒绝”,因为这将有助于引起注意客户端代码中的潜在错误,这些错误实际上导致无效值出现在程序中并到达构造函数。在大多数系统中,至少在开发过程中,通常都需要引起对bug的关注(除非在发生错误的情况下,这是系统的混乱特性)。
问题是您是否真的面对这种情况。
如果您的数据结构通常用于建模极坐标,则可以接受该值,因为超出-180和+180范围的角度并不是真正无效的。它们是完全有效的,并且它们恰好总是具有-180到+180范围内的等效项(如果您要将它们转换为目标范围,请放心使用-客户端代码通常不需要关心) 。
如果您的数据结构是对Web Mercator坐标进行显式建模(根据其初始形式的问题),那么最好遵循规范中提到的任何规定(我不知道,因此我不会赘述) 。如果您要建模的事物的规范表明某些值无效,请拒绝它们。如果说可以将它们解释为明智的事物(因此它们实际上是有效的),请接受它们。
用于发出是否接受值的信号的机制取决于语言的功能,其一般原理和性能要求。因此,您可能会抛出异常(在构造函数中)或返回结构的可为空版本(通过调用私有构造函数的静态方法),或者返回布尔值并将结构作为out
参数传递给调用方(再次通过调用私有构造函数的静态方法),依此类推。
这取决于很多。但是您应该决定做点事情并记录下来。
代码要做的唯一绝对错误的事情是忘记考虑用户输入可能超出预期范围,并编写意外具有某些行为的代码。因为这样一来,有些人会对您的代码的行为做出错误的假设,并且会导致错误,而另一些人最终会取决于您的代码意外具有的行为(即使该行为完全是愚蠢的),因此您将导致更多的错误。当您以后解决问题时。
在这种情况下,我可以以任何一种方式看到参数。如果某人从175度移动+10度,那么他们应该以-175结尾。如果您总是规范化用户输入,因此将185等效为-175,那么当客户端代码加10度时,客户端代码就不会做错事。它始终具有正确的效果。如果您将185视为错误,则在每种情况下都强制客户端代码将相对度添加到规范化逻辑中(或至少记得调用规范化过程),这实际上会导致错误(尽管希望很容易捕获,但很快就会解决)。但是,如果用户输入经度数,直接在程序中编写经度数或通过旨在始终位于[-180,180)中的某个过程计算出经度数,则该范围之外的值很可能表示错误,因此“进行转换可能会隐藏问题。
在这种情况下,我的理想情况可能是定义一个代表正确域的类型。使用抽象类型(不要让客户代码简单地访问其中的原始数字),并提供规范化工厂和验证工厂(以便客户进行权衡)。但是无论使用哪种类型的值,通过公共API看到的185应该与-175毫无区别(无论它们是在构造上转换还是提供相等性,访问器和其他以某种方式忽略差异的操作) 。
如果选择一种解决方案对您来说并不重要,则可以让用户决定。
给定您的结构是只读值对象并由方法/构造函数创建,您可以根据用户具有的选项提供两个重载:
另外,切勿让用户使用无效的结构来传递给您的其他方法,使其在创建时正确无误。
编辑:根据评论,我假设您正在使用c#。
catch
您的异常。正如其他人所说的那样,它是允许客户根据自己的意愿进行限制。您并没有真正绕过任何事情。
这取决于输入是直接通过某个UI来自用户还是来自系统。
通过UI输入
这是一个用户体验问题,如何处理无效输入。我不知道您的具体情况,但总的来说,有几种选择:
选择取决于用户的期望以及数据的重要性。例如,Google会自动修复查询中的拼写错误,但是这种风险低,因为无用的更改不是问题,而且易于修复(即使这样,结果页上仍清楚地表明查询已更改)。另一方面,如果要输入核导弹的坐标,则可能需要更严格的输入验证,并且不对无效数据进行静默修复。因此,没有普遍的答案。
最重要的是,您应考虑更正输入是否对用户有利。用户为什么要输入无效数据?很容易看出有人可能会犯拼写错误,但是为什么有人会输入-185的经度?如果用户的意思是+175,则可能输入了+175。我认为无效的经度很可能只是键入错误,而用户的意思是-85或其他。在这种情况下,无声转换是不利且无济于事的。对于您的应用程序,最用户友好的方法可能是提醒用户该无效值,并让用户自己对其进行更正。
通过API输入
如果输入来自其他系统或子系统,则毫无疑问。您应该抛出异常。切勿静默转换来自另一个系统的无效输入,因为它可能掩盖系统中其他位置的错误。如果输入已“更正”,则应在UI层中进行,而不是深入系统中。
对于开发人员而言,最方便的选择是在平台中提供编译时错误支持,以获取超出范围的值。在这种情况下,范围也应该是方法签名的一部分,就像参数的类型一样。如果您的方法签名定义为采用整数,则API用户无法传递String的方式相同,如果不检查该值是否在方法签名所给定的范围内,则用户不应传递值。如果不检查,他应该得到一个编译时错误,因此可以避免运行时错误。
但是目前,很少有编译器/平台支持这种类型的编译时间检查。因此,这是开发人员的头疼。但是理想情况下,您的方法应该针对不支持的值抛出有意义的异常,并清楚地记录下来。
顺便说一句,我真的很喜欢Joe Duffy 在这里提出的错误模型。
默认值应该是引发异常。您还可以允许类似strict=false
这样的选项,并根据标志进行强制转换,当然这strict=true
是默认设置。这很普遍:
strict=false
?
对我来说,最佳实践是永远不要更改用户输入。我通常采用的方法是将验证与执行分开。