为了在 Scala 中处理异常,我更喜欢避免使用基本的 try
/catch
并从 Scalaz 的 Validation
中受益(类似在某些情况下为 Either
类型)。
我的应用程序公开了一些服务。想象一下我的服务层中的这种方法(没有实际意义,但对概念有益)。它将 Buyer
(购买者)与他的新 Car
相关联,如果所有规则都成功通过,则返回包含此关联的 Car
:
def create(carDTO: CarDTO, buyerDTO: BuyerDTO): Validation[Either[TechnicalFailure, List[CarCreationFailure]], CarDTO]
说明:创建 Car
可能会导致两种异常类型之一:
- 技术故障(例如,当数据库崩溃时)包装
Throwable
异常。 - 业务失败(自定义应用程序的规则防止不一致,例如,
Car
处于非法状态)。CarCreationFailure
就是其中之一,当然可以通过更多精确
失败来扩展。
我的问题特别关注客户端,并专门处理不止一种潜在的业务失败
。
是否应该将返回类型 Validation[Either[TechnicalFailure, List[CarCreationFailure]], CarDTO]
替换为更简单的:ValidationNel[Throwable, CarDTO]
/p>
注意这里的ValidationNel
(将错误/异常累积到NonEmptyList
)。
一个缺点是,新读者乍一看无法猜测此方法返回 TechnicalFailure
或 CarCreationFailure
(BusinessFailure< 的子类
所以);只是一个太可怕的Throwable
。
他将被迫对我的应用程序中包含的每个 Throwable
类型应用模式匹配,以确保不会忘记任何人... => 困惑。
在这些解决方案中,最干净的方法是什么,或者……其他方法?
最佳答案
在这种情况下,我个人使用一个名为 Tri
的自定义类,它的工作方式类似于 Try
和 Either
:
sealed trait Tri [+Value, +Problem] { ... }
final case class Good[+Value](v: Value) extends Tri[Value, Nothing] { ... }
final case class Bad[+Problem](p: Problem) extends Tri[Nothing, Problem] { ... }
final case class Ugly(th: Throwable) extends Tri[Nothing, Nothing] { ... }
用适当的方法来处理人们想要的最常见的操作。在这种情况下,我会简单地返回给客户端
Tri[CarDTO, List[CarCreationFailure]]
并根据需要将异常包装到自定义异常类中。这两者都是关于代码本身正常工作时可能发生或不发生的事情的预先说明,并且像往常一样留下未说明的异常情况,这些情况迟早需要处理,包括数据库错误之类的事情。然后你只是例如:
create(car, buyer) match {
case Good(dto) => // Yay
case Bad(xs) => // Handle list of car creation failures
case Ugly(th) => // Handle exceptions
}
我不知道 Scalaz7 中有任何类似的功能,尽管您可以比在 Scalazless Scala 中更容易地构建这些片段(正如您所做的那样,但为什么不使用 \/
?),因此您可能不太愿意创建这种三方错误处理类。
关于api - 编写处理异常的 Scala 方法签名的最佳方式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16193302/