3

我的目标是在创建一个有效实例之前验证'方法User中的字段:objectapplyUser

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.
  }

}

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

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

让我们首先展示我的实际代码(作为参考,Empty****对象只是一些case object扩展UserCreationFailure):

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 并保留此验证机制?

-----更新----

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

4

1 回答 1

4

我不确定我是否理解为什么您首先需要专门的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.

于 2013-05-05T20:34:31.800 回答