Scala добавляет динамический переключатель на будущую карту - PullRequest
1 голос
/ 16 апреля 2019

В моем приложении есть шаблон, который я пытаюсь уменьшить, когда 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)
}

}

Ответы [ 2 ]

2 голосов
/ 16 апреля 2019

Как насчет того, чтобы сделать это так:

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) или для других случаев использования используйте фактический результат успеха в функции, чтобы сгенерировать соответствующий ответ. Таким образом, избегайте повторения совпадения и просто по-разному интерпретируйте результат в случае успеха и разные типы (извините, код следует рассматривать как своего рода псевдокод :))

1 голос
/ 17 апреля 2019

Лучший способ решить эту проблему - использовать частичную функцию и карри

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