3

我有以下验证方法:

def validate(wine: Wine): List[Error] = {

  var errors = List[Error]()

  if (Validate.isEmptyWord(wine.name)) {
    errors ::= ValidationError("name", "Name not specified")
  } else {
    if (isDuplicate(wine, "name")) {
      errors ::= ValidationError("name", "There already exists a wine with the name '%s'".format(wine.name))
    }
  }

  if (Validate.isEmptyWord(wine.grapes)) {
    errors ::= ValidationError("grapes", "Grapes not specified")
  }

  if (Validate.isEmptyWord(wine.country)) {
    errors ::= ValidationError("country", "Country not specified")
  }

  // more stuff like this and finnally
  errors.reverse
}

你明白了

您将如何修改它以避免 var List[Error] 并使其更具功能性?

4

2 回答 2

10

Scalaz提供了一个Validation类,可以很容易地用函数式方法解决这类问题。Validation此 Stack Overflow 答案中有一个更详细的示例说明如何使用,但我也会在这里给出一个草图,以展示它在您的情况下如何工作。我将假设以下设置:

case class Wine(name: String, grapes: String, country: String)
case class ValidationError(name: String, msg: String)

现在我们可以编写几个验证方法(注意我使用的是 Scalaz 7):

import scalaz._, Scalaz._

def checkNonempty(v: String, name: String, msg: String) =
  if (v.nonEmpty) v.successNel else ValidationError(name, msg).failNel

def checkDuplicate(v: String, name: String, msg: String) =
  if (true) v.successNel else ValidationError(name, msg).failNel

当然,您应该将自己的重复检查添加到最后一行。然后我们可以将它们包装在一起:

def createWine(name: String, grape: String, country: String) = (
  checkNonempty(name, "name", "Name not specified").flatMap(_ =>
    checkDuplicate(name, "name",
      "There already exists a wine with the name '%s'".format(name)
    )
  ) |@|
  checkNonempty(grape, "grape", "Grape not specified") |@|
  checkNonempty(country, "country", "Country not specified")
)(Wine.apply)

现在如果我们写这样的东西:

val result: ValidationNEL[ValidationError, Wine] = createWine(
  "Whatever Estates", "Whatever Grape", "U.S."
)

我们将获得一个成功值:

Success(Wine(Whatever Estates,Whatever Grape,U.S.))

但是如果我们给它无效的输入:

val result: ValidationNEL[ValidationError, Wine] = createWine(
  "", "Some Grape", ""
)

我们将获得累积错误的列表:

Failure(
  NonEmptyList(
    ValidationError(name,Name not specified),
    ValidationError(country,Country not specified)
  )
)

当然,您也可以使用自己的验证逻辑,但是如果您正在做很多此类事情,那么使用像 Scalaz 这样的库可能是值得的。

于 2012-09-22T21:29:11.613 回答
3

编写您的isX方法以返回 aOption[Error]而不是 a Boolean,然后

List(
  Validate.isEmptyWord(wine.name, "name", "Name not specified").
  orElse( Validate.isDuplicate(wine.name, "name", "There already exists...") ),
  Validate.isEmptyWord(wine.grapes, "grapes", "Grapes not specified"),
  ...
).flatten

你会有你的错误。此外,您还可以删除许多其他重复项(例如,如果始终是"x", "X not specified",您可以只提供"x"并让其余部分由 填充isEmptyWord)。

于 2012-09-22T20:47:45.467 回答