编写Scala方法签名处理异常的最佳方法

时间:2013-04-24 13:27:57

标签: api scala exception scalaz scalaz7

为了处理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是一个,当然可以通过更多precised失败来扩展。

我的问题特别关注客户端,并专门针对多个潜在Business Failure进行处理。

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

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

缺点是新读者乍一看不能猜测此方法会返回TechnicalFailureCarCreationFailureBusinessFailure的子类所以);只是太可怕了Throwable

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

这些解决方案中最干净的方法是什么,或者......其他解决方案?

2 个答案:

答案 0 :(得分: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更容易构建各个部分(就像你有,但为什么不使用\/?),所以你可能没那么积极创建这种三方错误处理类。

答案 1 :(得分:1)

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

所有业务异常/错误都包含在自定义BusinessException中,所有其他错误都会引发各种异常。

我们使用基本的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代码包装成抛出异常,这是我们的用例。