Scala 向未来地图添加动态切换
Scala adding dynamic switch to future map
在我的应用程序中,当 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 方法中。此外,这种方法似乎并没有删除太多样板,但我觉得另一种方法可能会。
更新解决方案示例
感谢大家的帮助。最后,我能够使用 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)
}
}
你这样做怎么样:
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)
或对于其他用例,使用函数内的实际成功结果来生成相应的响应。
这样可以避免重复匹配,只是对成功案例和不同类型的结果有不同的解释(对不起,该代码将被视为有点伪代码:))
解决这个问题的更好方法是使用偏函数和柯里化
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)
在我的应用程序中,当 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 方法中。此外,这种方法似乎并没有删除太多样板,但我觉得另一种方法可能会。
更新解决方案示例
感谢大家的帮助。最后,我能够使用 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)
}
}
你这样做怎么样:
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)
或对于其他用例,使用函数内的实际成功结果来生成相应的响应。
这样可以避免重复匹配,只是对成功案例和不同类型的结果有不同的解释(对不起,该代码将被视为有点伪代码:))
解决这个问题的更好方法是使用偏函数和柯里化
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)