Scala将动态开关添加到未来的地图中



在我的应用程序中,当 API 调用适当的服务方法时,我试图减少一些样板。

下面是一个抽象的例子:

override def foo(in: FooRequest): Future[FooResponse] =
   commandService
     .doFoo(in)
     .map {
       case Success(_) => FooResponse()
       case Failure(e) => failureHandler(Status.INTERNAL, e)
     }
     .recover { case e: Throwable => failureHandler(Status.INTERNAL, e) }

override def bar(in: BarRequest): Future[BarResponse] =
   commandService
     .doBar(in)
     .map {
       case Success(_) => BarResponse()
       case Failure(e) => failureHandler(Status.INTERNAL, e)
     }
     .recover { case e: Throwable => failureHandler(Status.INTERNAL, e) }
20times

因此,正如您所看到的,这里有一些应用DRY原则的机会。

我可以创建一个函数,该函数

接受服务方法作为函数,然后执行样板操作,但我不知道如何在 case 语句中工作到未来的地图中。

private def executeCommand[A, B, C](
   command: A, 
   f: A => Future[Try[B]], 
   onSuccess: Try[B] => C): Future[C] =
f(command)
  .map(onSuccess)
  .recover { case e: Throwable => failureHandler(Status.INTERNAL, e) }

但这需要我像这样调用该方法:

def foo(in: FooRequest) =
   executeCommand(
     in,
     commandService.doFoo, { x: Try[FooSuccess] =>
        x match {
            case Success(_) => FooResponse(in.requestId)
            case Failure(e) => failureHandler(Status.INTERNAL, e)
       }
     }

将针对每种方法重复失败情况。如果可能的话,我想将其添加到 executeCommand 方法中。此外,这种方法似乎并没有删除太多样板,但我觉得另一种方法可能会。

使用解决方案示例进行更新

感谢大家的帮助。最后,我能够使用阿瓦根的答案找到一个很好的解决方案。

def foo(in: FooRequest) = 
   commandService.doFoo(in).handleResults((pass: FooSuccess) => FooResponse(pass.requestId))
//20 times
implicit private class FooBarServiceHandler[A](future: Future[Try[A]]) {
   import scala.language.higherKinds
   def handleResults[B](func: A => B): Future[B] =
      future.map(onSuccess(func)).recover { case e: Throwable => failureHandler(Status.INTERNAL, e) }
   private def onSuccess[B, C](func: B => C): Try[B] => C = {
      case Success(resp) => func(resp)
      case Failure(e)    => failureHandler(Status.INTERNAL, e)
}

}

你这样做怎么样:

def onSuccess[B,C](func: B => C): Try[B] => C = {
  {
            case Success(resp) => func.apply(resp)
            case Failure(e) => failureHandler(Status.INTERNAL, e)
 }
}

并传递function resp => FooResponse(in.requestId)或对于其他用例,使用函数内的实际成功结果来生成相应的响应。这样ud避免重复匹配,只是在成功案例和不同类型的结果中有不同的解释(对不起,代码被视为伪代码:)(

解决此问题的更好方法是使用偏函数和柯里

def executeCommand[A, B, C](f: A => Future[Try[B]])
                         (handleFailure: PartialFunction[Try[B], C])
                         (handleSuccess: PartialFunction[Try[B], C])
                         (command: A)
                         (implicit executionContext: ExecutionContext): Future[C] = {
     val handleResult = handleSuccess.orElse(handleFailure)
     f(command).collect(handleResult)
}.recover{
case ex: Throwable => failureHandler(Status.INTERNAL, e)
}

val failureHandlerPf = {
    case Failure(e) => failureHandler(Status.INTERNAL, e)
}
val successHandlerFooPf = {
    x: FooResponse => x
}
val func1 = executeCommand(failureHandlerPf)
val fooPf = func1(successHandlerFooPf)
override def foo = fooPf(in)
val successHandlerBarPf = {
    case x: BarResponse => x
}
val barPf = func1(successHandlerBarPf)
override def bar = barPf(in)

最新更新