Scala异常作为函数参数设计模式
我正在编写一个web应用程序,其中使用异常来处理错误案例。我经常发现自己在写这样的助手:Scala异常作为函数参数设计模式,scala,exception,types,Scala,Exception,Types,我正在编写一个web应用程序,其中使用异常来处理错误案例。我经常发现自己在写这样的助手: def someHelper(...) : Boolean {...} if (!someHelper(...)){ throw new SomeException() } 然后像这样使用它: def someHelper(...) : Boolean {...} if (!someHelper(...)){ throw new SomeException() } 这些异常表示无效参
def someHelper(...) : Boolean {...}
if (!someHelper(...)){
throw new SomeException()
}
然后像这样使用它:
def someHelper(...) : Boolean {...}
if (!someHelper(...)){
throw new SomeException()
}
这些异常表示无效参数之类的情况,当处理这些异常时,它们会向用户发送有用的错误消息,例如
try {
...
} catch {
case e: SomeException => "Bad user!"
}
这是否合理的做法?我如何将异常传递到helper函数并将其抛出?我在为这样的函数构造类型时遇到困难。将异常实例传递给某个方法没有什么特别之处:
def someMethod(e: SomeException) {
throw e
}
someMethod(new SomeException)
但我必须说我有一种非常明显的感觉,你的整个想法都有味道。如果要验证用户输入,只需编写验证器,例如,
UserValidator
,该验证器将使用类似于isValid
的方法来测试用户输入并返回布尔值,您也可以在那里实现一些消息传递。例外情况实际上是为了不同的目的 将异常实例传递给某个方法没有什么特别之处:
def someMethod(e: SomeException) {
throw e
}
someMethod(new SomeException)
但我必须说我有一种非常明显的感觉,你的整个想法都有味道。如果要验证用户输入,只需编写验证器,例如,
UserValidator
,该验证器将使用类似于isValid
的方法来测试用户输入并返回布尔值,您也可以在那里实现一些消息传递。例外情况实际上是为了不同的目的 两种最常见的方法是让助手自己创建并抛出异常,或者让调用代码检查结果,如果需要,抛出有意义的异常
我从来没有见过一个库,在那里你通过了你期望助手抛出的异常。正如我所说,简单地实例化一个异常会带来惊人的巨大成本,如果您在整个代码中遵循此模式,您可能会看到一个整体性能问题。这可以通过使用来缓解,但是如果您只是忘记在几个关键函数中添加=>
,那么您就会遇到一个很难跟踪的性能问题
在一天结束时,如果您希望帮助器抛出异常,那么帮助器本身已经知道它想要抛出什么样的异常是有意义的。如果我必须在A和B之间选择:
def helperA(...) { if (stuff) throw new InvalidStuff() }
def helperB(..., onError: => Exception) { if (stuff) throw onError }
每次我都会选择一个
现在,如果我必须在A和你现在拥有的之间做出选择,那是一个折腾。这实际上取决于上下文、您试图用助手完成的任务、它们的其他用途等
最后,在这种情况下,命名非常重要。如果您选择返回代码助手路线,那么助手应该有问题名称,例如
isValid
。如果您有异常抛出帮助程序,它们应该有操作名称,例如validate
。甚至可能会强调它,比如validate\ux代码>处理您尝试执行的操作的两种最常见的方法是,让助手自己创建并引发异常,或者让调用代码检查结果,并在需要时引发有意义的异常
我从来没有见过一个库,在那里你通过了你期望助手抛出的异常。正如我所说,简单地实例化一个异常会带来惊人的巨大成本,如果您在整个代码中遵循此模式,您可能会看到一个整体性能问题。这可以通过使用来缓解,但是如果您只是忘记在几个关键函数中添加=>
,那么您就会遇到一个很难跟踪的性能问题
在一天结束时,如果您希望帮助器抛出异常,那么帮助器本身已经知道它想要抛出什么样的异常是有意义的。如果我必须在A和B之间选择:
def helperA(...) { if (stuff) throw new InvalidStuff() }
def helperB(..., onError: => Exception) { if (stuff) throw onError }
每次我都会选择一个
现在,如果我必须在A和你现在拥有的之间做出选择,那是一个折腾。这实际上取决于上下文、您试图用助手完成的任务、它们的其他用途等
最后,在这种情况下,命名非常重要。如果您选择返回代码助手路线,那么助手应该有问题名称,例如isValid
。如果您有异常抛出帮助程序,它们应该有操作名称,例如validate
。甚至可能会强调它,比如validate\ux代码>我大部分时间都使用或者,没有例外。我通常使用异常,正如您所做的或类似的方法,当控制流必须返回到某个遥远的点时,否则就没有什么明智的事情可以做。但是,当异常可以在本地公平地处理时,我将改为
def myMethod(...): Either[String,ValidatedInputForm] = {
...
if (!someHelper(...)) Left("Agree button not checked")
else Right(whateverForm)
}
然后当我调用这个方法时,我可以
myMethod(blah).fold({ err =>
doSomething(err)
saneReturnValue
}, { form =>
foo(form)
form.usefulField
})
或者在左侧(err)
与右侧(form)
或其他各种情况下进行匹配
如果我不想马上处理错误,而是想处理返回值,那么
myMethod(blah).right.map{ form =>
foo(form)
bar(form)
}
我将得到一个,如果它是一个错误消息,则错误消息保持为左
,或者如果它正常,则将{foo(form);bar(form)}
的结果作为右
。您还可以使用flatMap
链接错误处理,例如,如果您想对迄今为止的正确值执行额外检查并拒绝其中一些值,您可以
myMethod(blah).right.flatMap{ form =>
if (!checkSomething(form)) Left("Something didn't check out.")
else Right(form)
}
正是这种处理方式使得使用或比异常更方便(如果异常很常见,通常表现更好),这就是我使用它们的原因
(事实上,在很多情况下,我不在乎为什么会出问题,只在乎它出了问题,在这种情况下,我只使用一个选项)我大部分时间都使用或者,而不是例外。我通常使用异常,正如您所做的或类似的方法,当控制流必须返回到某个distan时