对于可能不返回任何结果的API,使用Future.successful(None)vs Future.failed(new Exception())?

时间:2017-09-28 14:44:10

标签: scala future api-design

Scala中输入可能会或可能不会返回结果的API的惯用方法是什么? 我可以简单地输入到Future [Option [A]]并在没有结果的情况下返回None,但这会让我认为的API客户端更加冗长,因为它必须进行一些模式匹配(或其他技术,如折叠) )返回选项。

trait SomeAPI {
  def fetch(): Future[Option[String]]
}

object APIImpl {

  def asyncDBCall(): Future[List[String]] = ???

  override def fetch(): Future[Option[String]] = asyncDBCall().map(r => if (r.isEmpty) None else Some(r.head)
}

但是,期货已经给了我们一种说法,没有结果让它失败的方法。但我不知道这是否是一种恰当或富有表现力的方式,表示该呼叫不会返回结果。

trait SomeAPI {
    def fetch(): Future[String]

}


class NoResultsException extends Exception

object APIImpl extends SomeAPI {

  def asyncDBCall(): Future[List[String]] = ???

  override def fetch(): Future[String] = asyncDBCall().map(r => if (r.isEmpty) throw new NoResultsException() else r.head)
}

起初似乎两种选择都是可以接受的,最终的决定可能只是个人偏好,但也许我错过了一些东西并且让未来失败不是一个好选择。

3 个答案:

答案 0 :(得分:4)

应该为错误保留例外情况。如果API可能会或可能不会返回结果,并且两种情况都被认为是正常的,那么API应该使用Option

如果通过强制客户端处理这两种情况使其更加冗长,那就更好了,因为客户端应该处理这两种情况。当您不想强制客户端代码处理异常情况(意外错误)时,例外情况很好,该情况应该级联并由错误处理程序处理。

现在,如果缺乏结果确实表明出现了问题,那么异常就是合适的。

答案 1 :(得分:1)

我希望客户想要对待"成功,没有结果"一般而言,失败的方式不同(例如,在第二种情况下重试,但不是第一次重试)。如果你表示第一个失败的未来,那么你会为客户更糟糕做出冗长,而不是更好:他们只需要检查异常而不是成功,而API并没有帮助他们要注意像Future[Option[A]]那样。

如果您真的担心详细程度,可以添加自定义提取器或方法供客户端使用(如果需要),例如:

implicit class FutureOption[A](future: Future[Option[A]]) {
  def mapOpt[B](handleSuccessWithResult: A => B, handleSuccessWithNoResult: () => B) = Future.map {
    case Some(x) => onSuccessWithResult(x)
    case None => onSuccessWithNoResult()
  }
}

答案 2 :(得分:0)

其他人是对的。如果这是常规操作的一部分,那么失败并不是描述缺乏具体结果的好方法。

Scala为您提供了另一种明确注意的可能性,即可能没有结果,密封的特征。

Object

然后,用户可以进行模式匹配。如果您有多种返回类型(不仅仅是两种)而且没有自然的空返回类型(如sealed trait MyOperationReturn case object OkNoResult case class OkWithStringResponse(data: String) extends MyOperationReturn case class OkWithIntResponse(data: Int) extends MyOperationReturn

,则此选项特别方便