简体   繁体   English

Scala将动态切换添加到未来的地图中

[英]Scala adding dynamic switch to future map

In my application there is some boilerplate I'm trying to reduce when the API calls appropriate service methods. 在我的应用程序中,当API调用适当的服务方法时,我试图减少一些样板。

Here is an abstract example: 这是一个抽象的例子:

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

So as you can see there is some opportunity to apply DRY principals here. 所以你可以看到有机会在这里申请DRY校长。

I could create a function that accepts a the service method as a function and then perform the boilerplate actions, but I don't know how to work in a case statement into the future map. 我可以创建一个函数来接受服务方法作为函数,然后执行样板操作,但我不知道如何在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) }

But this would require me to call the method like this: 但这需要我调用这样的方法:

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

The Failure case would be repeated for each method. 每种方法都会重复失败案例。 I would like to add that to the executeCommand method if possible. 如果可能的话,我想将它添加到executeCommand方法中。 Also, this approach seems like it doesn't remove much boilerplate, but I feel like another approach might. 此外,这种方法似乎并没有删除太多样板,但我觉得另一种方法可能。

UPDATE WITH SOLUTION EXAMPLE 更新解决方案示例

Thanks to all for your help. 感谢大家的帮助。 In the end I was able to find a pretty good solution using awagen's answer. 最后,我能够使用awagen的答案找到一个非常好的解决方案。

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

} }

How about you do it like this: 你怎么样这样做:

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

and pass the function resp => FooResponse(in.requestId) or for other use cases use the actual Success result within the function to generate the respective response. 并传递function resp => FooResponse(in.requestId)或其他用例使用函数中的实际Success结果来生成相应的响应。 This way ud avoid repeating the match and just have different interpretation of the result in success case and different types (excuse me, the code is to be seen as kinda pseudocode :) ) 这种方式可以避免重复匹配,只是对成功案例和不同类型的结果有不同的解释(对不起,代码可以看作有点伪代码:))

A better way to solve this is to use partial function and currying 解决这个问题的更好方法是使用部分功能和currying

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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM