在这个时刻我的设计面临着一个重大问题。我的方法是尝试完成以下任务:
我想以灵活的方式设计它,因为要求是不变的,我不想继续根据任何变化修改我的逻辑。我确实意识到一些变化是不可避免的,但我希望尽量减少损害并尊重开放原则。
我最初的观点如下:
def complexOperation(someObject:T) =
dbService.insertIntoDb(someObject) match {
case Left(e:Exception) => Left(e)
case Right(id:Int) => webService.callWebService1(id,someObject) match {
case Left(e:Exception) => Left(e)
case Right(r:SomeResponse1) => webService.callWebservice2(r,someObject) match {
case Left(e:Exception) => webService.rollbackService1();Left(e)
case Right(context:ResponseContext) => dbService.insertContextIntoDb(context) match {
case Left(e:Exception) => Left(e)
case Right(id:Int) => webService.callWebservice3(id,someObject) match {
case Left(e:Exception) => webService.rollbackService3();Left(e)
case Right(r:Response) => Right(r)
}
}
}
}
正如你所看到的,这是一个混乱的混乱。如果事情失控,我既不能对它进行单元测试,也不能扩展它,也不能很容易地调试它。这段代码可以达到它的目的,但是对于如何重构它以使继承我的代码的人的生活变得更加容易,我会很高兴。
谢谢
答案 0 :(得分:2)
您可以使用理解来减少代码中的噪音。
答案 1 :(得分:2)
看看scala.util.Try。它可以在Scala 2.10中使用,作为一个选项可能会或可能不会,但它的想法非常适合您的场景。
你在代码示例中所拥有的就是我喜欢称之为嵌套的“金字塔”。对此最好的解决方案是尽可能使用平面映射。但显然,当你在每一步都有Either[Exception, Result]
这样的东西时,这就是一个问题。这就是Try
的用武之地。Try[T]
基本上是Either[Exception, T]
的替代品,它带有您需要的所有flatMap
善良。
假设您可以更改这些webService
调用的返回类型,或者提供从Either[Exception, Result]
到Try[Result]
的隐式转换,您的代码块将变得更像...... < / p>
for {
id <- dbService.insertIntoDb(someObject)
r <- webService.callWebService1(id,someObject)
context <- webService.callWebservice2(r,someObject)
id2 <- dbService.insertContextIntoDb(context)
response <- webService.callWebservice3(id,someObject).recoverWith {
case e: Exception => webService.rollbackService3(); Failure(e)
}
} yield response
Lift在net.liftweb.common.Box中有类似的机制。它就像Option
,但也有一个容器用于例外。
修改:看起来您可以使用left
的{{1}}或right
方法,它可以让您使用Either
- 几乎就是我用flatMap
描述的方式。唯一的区别是最终结果是Try
而不是Either[Exception, Result]
。查看LeftProjection了解详细信息/示例。