validation - 使用 Scalaz 7 在 Scala 中验证类的内聚方式

标签 validation scala single-responsibility-principle scalaz7

我的目标是在创建一个有效的用户对象apply方法中的User字段实例:

case class User(String userName, String password)

object User {

  def apply(userValidator: UserValidator): ValidationNel[UserCreationFailure, User] = {
    //call UserValidator's validate() method here and initialize effective User instance.
  }

}

我选择使用 Scalaz7 中的Validation 来累积潜在的非法参数/错误。

以下代码中的一个缺点是 Scalaz7 API 强制我让验证器创建自己的实例。但是,通过遵循单一职责原则,这显然不是它的职责。它的作用只是验证字段并返回一些错误列表。

让我们首先展示我的实际代码(供引用,Empty**** 对象只是一些扩展UserCreationFailurecase 对象):

class UserValidator(val userName: String, val password: String)
  extends CommonValidator[UserCreationFailure] {

  def validate(): ValidationNel[UserCreationFailure, User] = {
    (checkForUserName ⊛
      checkForPassword)((userName, password) => new User(userName, password)
  }

  private def checkForUserName: ValidationNel[UserCreationFailure, String] = {
    checkForNonEmptyString(userName) {
      EmptyUserName
    }
  }

  def checkForPassword: ValidationNel[UserCreationFailure, String] = {
    checkForNonEmptyString(password) {
      EmptyPassword
    }
  }
}

我期望的只是返回这段代码:

(checkForUserName ⊛ checkForPassword)

并将适当的结果带入我的 User 类,允许通过以下方式创建有效实例:

def apply(userValidator: UserValidator): ValidationNel[UserCreationFailure, User] = {
        userValidator(username, password).validate()((userName, password)(new User(userName, password))
 }

的确,用SRP会更友好。

但是 (checkForUserName ⊛ checkForPassword) 返回一个完全 private 类型的类型:

private[scalaz] trait ApplicativeBuilder[M[_], A, B],

因此我不知道返回的 class 类型。

因此,我不得不直接将用户的创作与它联系起来。

我怎样才能保留 SRP 并保留这种验证机制?

-----更新-----

正如@Travis Brown 提到的,为我的 UserValidator 使用外部 class 的意图可能看起来很奇怪。实际上,我希望验证器是可模拟的,因此,我不得不在 trait/abstract class 上使用组合。

最佳答案

我不确定我首先理解为什么您需要专用的 UserValidator 类。在这种情况下,我更有可能将我所有的通用验证代码捆绑到一个单独的特征中,并让我的 User 伴随对象(或我想负责创建的任何其他部分User 实例)扩展了该特征。这是一个快速草图:

import scalaz._, Scalaz._

trait Validator[E] {
  def checkNonEmpty(error: E)(s: String): ValidationNel[E, String] =
    if (s.isEmpty) error.failNel else s.successNel
}

sealed trait UserCreationFailure
case object EmptyPassword extends UserCreationFailure
case object EmptyUsername extends UserCreationFailure

case class User(name: String, pass: String)

object User extends Validator[UserCreationFailure] {
  def validated(
    name: String,
    pass: String
  ): ValidationNel[UserCreationFailure, User] = (
    checkNonEmpty(EmptyUsername)(name) |@| checkNonEmpty(EmptyPassword)(pass)
  )(apply)
}

然后:

scala> println(User.validated("", ""))
Failure(NonEmptyList(EmptyUsername, EmptyPassword))

scala> println(User.validated("a", ""))
Failure(NonEmptyList(EmptyPassword))

scala> println(User.validated("", "b"))
Failure(NonEmptyList(EmptyUsername))

scala> println(User.validated("a", "b"))
Success(User(a,b))

如果您有大量特定于 User 的验证逻辑,并且不想污染您的 User 对象,我想您可以将其分解为 UserValidator 特征将扩展您的通用 Validator 并由 User 扩展。

关于validation - 使用 Scalaz 7 在 Scala 中验证类的内聚方式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16386513/

相关文章:

angular - 为什么 Angular 验证是在组件/表单中完成的,而不是在域模型上完成的?

javascript - 如何在 ReactJS 中验证输入类型 ="number"?

java - 正则表达式 - 除了给定单词之外的所有有效标识符?

c++ - 如何使用单一职责原则

oop - 信息专家和告诉不要问与单一职责原则不一致吗?

Laravel:如何在表单请求输入中记录错误

c# - ContentControl 中的 GroupBox - 支持由绑定(bind)到 ContentControl 的内容实现的 IDataErrorInfo

scala - 如何在 slick 中使用共享 session 进行多个数据库操作?

scala - Scala的 “powerful”类型系统是什么?

oop - 单一职责原则的范围是什么?