在构建时抛出异常通常被视为不太礼貌的事情,但我不确定在尝试对case类值强制执行约束时如何避免这种情况.
比如说我需要表示一个范围,并且两个边界必须是正的.立即实施将是:
case class Range(from: Int, to: Int)
Run Code Online (Sandbox Code Playgroud)
然而,这并没有确保这两个from和to是积极的,也不是to大于from.
我的第一直觉是按如下方式实施:
case class Range(from: Int, to: Int) {
require(from >= 0)
require(to >= 0)
require(to >= from)
}
Run Code Online (Sandbox Code Playgroud)
然而,这使得Range构造函数不安全.
是否存在一种通用模式来保持案例类的易用性,强制实施值约束并避免抛出异常?
这是非常主观的,但我会尝试.
这取决于你的班级是如何构建的.对于在内部代码中随意使用的简单实用程序范围类,任何使用它的程序员都应该知道,输入范围错误的值可能会导致奇怪的结果.你可以假设这些类的合理使用确实没有遇到这个问题,并且在这些罕见的情况下抛出异常可能不是一个不好的做法,表明有人真的搞砸了.如果从背景中可以明显看出应该给课程带来什么价值,那么如果人们不能满足这些理智的期望,我认为抛出异常并不是特别糟糕.
将此与scala中有时仍会发生的NullPointerExceptions或ArithmethicExceptions 的行为进行比较.当面对足够的"精神错乱"时,有时候进行防御性打算是不合理的.
另一方面,如果您的课程由值控制较少的值填充,即它们是用户输入的直接后果,则您必须更好地控制您的构造.使用返回an Option或an 的函数创建一个伴随对象Either:
object Range {
def createSafe(from: Int, to: Int): Option[Range] = {
if(from >= 0 && to >= 0 && to >= from)
Some(Range(from, to))
else
None
}
}
Run Code Online (Sandbox Code Playgroud)
然后,您甚至可以通过覆盖apply建议的其他答案来重用案例类的实例化中的验证逻辑.
object Range {
def createSafe ...
def apply(from: Int, to: Int): Range = {
createSafe(from, to).getOrElse(throw new IllegalArgumentException(...))
}
}
Run Code Online (Sandbox Code Playgroud)
您可以重载apply伴随对象中的运算符以实现您想要做的事情:
object Range{
def apply(from: Int, to: Int) ={
val _from = Math.max(0, from)
val _to = Math.max(0, to)
if(_from < _to) new Range(_from, _to)
else new Range(_to, _from)
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,to用作变量名可能会导致一些“有趣”的结果。