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)
}
起初似乎两种选择都是可以接受的,最终的决定可能只是个人偏好,但也许我错过了一些东西并且让未来失败不是一个好选择。
答案 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
)