3

为了在 Scala 中处理异常,我更喜欢避免基本的try/catch并从 Scalaz 的函数式思维中受益(在某些情况下Validation类似于类型)。Either

我的应用程序公开了一些服务。想象一下我的服务层中的这种方法(没有真正意义,但对概念有好处)。如果所有规则都成功通过,它将Buyer(购买者)与他的新关联Car并返回包含此关联:Car

def create(carDTO: CarDTO, buyerDTO: BuyerDTO): Validation[Either[TechnicalFailure, List[CarCreationFailure]], CarDTO]

说明:创建 aCar可能会导致两种异常类型之一:

  • Throwable包装异常的技术故障(例如,当数据库崩溃时) 。
  • 业务失败(防止不一致的自定义应用程序规则,例如,Car具有非法状态)。CarCreationFailure是一个,当然可以通过更多的precised失败来扩展。

我的问题特别关注客户端,并专门处理不止一种潜力Business Failure

是否应该将返回类型Validation[Either[TechnicalFailure, List[CarCreationFailure]], CarDTO]替换为不那么繁琐的:ValidationNel[Throwable, CarDTO]

请注意此处ValidationNel(将错误/异常累积到 a 中NonEmptyList)。

缺点是新读者乍一看无法猜测此方法返回TechnicalFailureCarCreationFailureBusinessFailureso 的子类);只是一个太可怕了Throwable

他将被迫对Throwable我的应用程序中包含的每种类型应用模式匹配,以确保不会忘记任何人... => 混乱。

这些解决方案中最干净的方法是什么,或者可能是……另一种?

4

2 回答 2

2

在这样的情况下,我个人使用一个名为的自定义类Tri,它的工作方式类似于TryEither

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 中更容易地构建这些部分(正如您所拥有的,但为什么不使用?),因此您可能不太愿意创建这种三方错误处理类.

于 2013-04-24T15:59:04.520 回答
1

我们使用类似于 Rex Kerr 使用的东西(更简单但不太优雅)。

所有业务异常/错误都包含在 customBusinessException中,所有其他错误都抛出各种异常。

我们使用基本Try的,servlet 的代码看起来像(非常简化)

    ( req getParameter "action" match {

        case null          ⇒ Failure( new BusinessException( "Missing required parameter 'action'" ) )
        case "doThis"  ⇒ doThis()
        case "doThat"  ⇒ doThat( req )
        case x         ⇒ Failure( new BusinessException( "Uknown Action '" + x + "'" ) )
    } ) match {

        case Success( someResponse ) ⇒ //send 200 with some response
        case Failure(t) if t.isInstanceOf[BusinessException] => //send 400 with exception message 
        case Failure( t )        ⇒ //send 500 with exception message
    }

诸如doThis具有类型签名的方法

def doThis(): Try[ Option[ String ] ] = Try {
    //do stuff
}

这种简单机制的优点是很容易包装现有的抛出异常的 Java 代码,这就是我们的用例。

于 2013-04-24T17:07:21.460 回答