什么是基于规则验证的 Scala 惯用方法?
What's a good Scala idiomatic approach to rule-based validation
是否有惯用的解决方案来应用一系列业务规则,例如,来自传入的 JSON 请求。 "traditional" Java 方法非常 if-then
激烈,Scala 必须提供更好的解决方案。
我对模式匹配进行了一些试验,但还没有真正想出一个行之有效的模式。 (总是,我最终得到荒谬的嵌套 match
语句)...
这是我正在尝试做的一个荒谬的简单示例:
if (dateTime.isDefined) {
if (d == None)
// valid, continue
if (d.getMillis > new DateTime().getMillis)
// invalid, fail w/ date format message
else
if (d.getMillis < new DateTime(1970).getMillis)
// invalid, fail w/ date format message
else
// valid, continue
} else
// valid, continue
if (nextItem.isDefined) {
// ...
}
我在想也许是一种使用一系列链接的方法 Try()
...但似乎这种模式一定已经存在了。
有很多方法可以做到这一点。根据要采取的条件和操作,可以遵循不同的设计模式。
对于您在示例中勾画的轮廓,模式匹配可能是重写代码的最佳方式。
你问题中的问题是 Scala 中没有真正的惯用等价物。您需要以完全不同的方式解决问题,因为在 Scala 中,以这种方式评估一系列条件被认为是低效和直率的。
你的意思是这样的吗?
def test(dateTime: Option[DateTime], nextItem: Option[String]) {
(dateTime, nextItem) match {
case (Some(time), _) if time.getMillis > new DateTime().getMillis =>
//do something
case (Some(time), _) if time.getMillis > new DateTime(1970).getMillis =>
//do something
case (Some(time), _) =>
//do something else
case (None, Some(next)) =>
//do something else
}
}
当然你也可以使用带有选项的理解
val dateTime: Option[DateTime] =
for {
date <- getDate()
time <- getTime()
} yield new DateTime(date, time)
test(dateTime, nextItem)
链式尝试的方式有很多种
这是一个
def validate[A](elem: A): Try[Unit] = {
???
}
def test[A](thingsToValidate: Iterable[A]): Try[Unit] = {
thingsToValidate.view // view makes the whole thing lazy
.map { // so we don't validate more than necessary
case elem: String => validateString(elem)
case elem: Int => validateInt(elem)
}.find(_.isFailure)
.getOrElse(Success(Unit))
}
如果事物的数量是固定的,你也可以使用 for comprehension
def test(a: String, b: String, c: String): Try[Unit] = {
for {
_ <- validate(a)
_ <- validate(b)
_ <- validate(c)
} yield(Unit)
}
或使用例外
def test(a: String, b: String, c: String): Try[Unit] = {
try {
validate(a).get
validate(b).get
validate(c).get
Success(Unit)
} catch {
case e: Throwable => Failure(e)
}
}
是否有惯用的解决方案来应用一系列业务规则,例如,来自传入的 JSON 请求。 "traditional" Java 方法非常 if-then
激烈,Scala 必须提供更好的解决方案。
我对模式匹配进行了一些试验,但还没有真正想出一个行之有效的模式。 (总是,我最终得到荒谬的嵌套 match
语句)...
这是我正在尝试做的一个荒谬的简单示例:
if (dateTime.isDefined) {
if (d == None)
// valid, continue
if (d.getMillis > new DateTime().getMillis)
// invalid, fail w/ date format message
else
if (d.getMillis < new DateTime(1970).getMillis)
// invalid, fail w/ date format message
else
// valid, continue
} else
// valid, continue
if (nextItem.isDefined) {
// ...
}
我在想也许是一种使用一系列链接的方法 Try()
...但似乎这种模式一定已经存在了。
有很多方法可以做到这一点。根据要采取的条件和操作,可以遵循不同的设计模式。
对于您在示例中勾画的轮廓,模式匹配可能是重写代码的最佳方式。
你问题中的问题是 Scala 中没有真正的惯用等价物。您需要以完全不同的方式解决问题,因为在 Scala 中,以这种方式评估一系列条件被认为是低效和直率的。
你的意思是这样的吗?
def test(dateTime: Option[DateTime], nextItem: Option[String]) {
(dateTime, nextItem) match {
case (Some(time), _) if time.getMillis > new DateTime().getMillis =>
//do something
case (Some(time), _) if time.getMillis > new DateTime(1970).getMillis =>
//do something
case (Some(time), _) =>
//do something else
case (None, Some(next)) =>
//do something else
}
}
当然你也可以使用带有选项的理解
val dateTime: Option[DateTime] =
for {
date <- getDate()
time <- getTime()
} yield new DateTime(date, time)
test(dateTime, nextItem)
链式尝试的方式有很多种 这是一个
def validate[A](elem: A): Try[Unit] = {
???
}
def test[A](thingsToValidate: Iterable[A]): Try[Unit] = {
thingsToValidate.view // view makes the whole thing lazy
.map { // so we don't validate more than necessary
case elem: String => validateString(elem)
case elem: Int => validateInt(elem)
}.find(_.isFailure)
.getOrElse(Success(Unit))
}
如果事物的数量是固定的,你也可以使用 for comprehension
def test(a: String, b: String, c: String): Try[Unit] = {
for {
_ <- validate(a)
_ <- validate(b)
_ <- validate(c)
} yield(Unit)
}
或使用例外
def test(a: String, b: String, c: String): Try[Unit] = {
try {
validate(a).get
validate(b).get
validate(c).get
Success(Unit)
} catch {
case e: Throwable => Failure(e)
}
}