使用依赖项在scala代码中调用外部服务

时间:2012-10-27 23:16:19

标签: web-services scala design-patterns io pattern-matching

在这个时刻我的设计面临着一个重大问题。我的方法是尝试完成以下任务:

  1. 将传入的对象插入数据库。
  2. 从插入中获取自动增量的id,并使用它来调用webservice1和对象。
  3. 从webservice1获取结果,并使用原始对象和webservice1的一些响应调用webservice2。
  4. 合并来自webservice1和2的结果并将其写入数据库。
  5. 从最后一次插入中获取生成的自动增量ID,并使用原始对象调用webservice3,最终会导致操作成功或失败。
  6. 我想以灵活的方式设计它,因为要求是不变的,我不想继续根据任何变化修改我的逻辑。我确实意识到一些变化是不可避免的,但我希望尽量减少损害并尊重开放原则。

    我最初的观点如下:

    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)
                }
              }
            } 
         }
    

    正如你所看到的,这是一个混乱的混乱。如果事情失控,我既不能对它进行单元测试,也不能扩展它,也不能很容易地调试它。这段代码可以达到它的目的,但是对于如何重构它以使继承我的代码的人的生活变得更加容易,我会很高兴。

    谢谢

2 个答案:

答案 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了解详细信息/示例。