Validation 使用Scalaz 7在Scala中验证类的内聚方法

Validation 使用Scalaz 7在Scala中验证类的内聚方法,validation,scala,single-responsibility-principle,scalaz7,Validation,Scala,Single Responsibility Principle,Scalaz7,我的目标是在创建一个有效的用户实例之前,验证对象的应用方法中的用户字段: case class User(String userName, String password) object User { def apply(userValidator: UserValidator): ValidationNel[UserCreationFailure, User] = { //call UserValidator's validate() method here and initi

我的目标是在创建一个有效的
用户
实例之前,验证
对象
应用
方法中的
用户
字段:

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

}
我选择使用
Validation
fromScalaz7来积累潜在的非法参数/错误

下面代码中的一个缺点是ScalaZ7API强迫我让验证器自己创建实例。然而,通过遵循单一责任原则,它显然不是它的角色。它的作用是验证字段并返回一些错误列表

让我们首先介绍一下我的实际代码(作为参考,
空****
对象只是一些
案例对象
扩展
用户创建失败
):

我希望只返回以下代码片段:

(checkForUserName ⊛ checkForPassword)
并将适当的结果带到my
User
类中,允许通过执行以下操作创建有效实例:

def apply(userValidator: UserValidator): ValidationNel[UserCreationFailure, User] = {
        userValidator(username, password).validate()((userName, password)(new User(userName, password))
 }
事实上,它将与SRP更加友好

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

private[scalaz]trait ApplicativeBuilder[M[\uz],A,B]

因此,我无法掌握返回的
类的类型

因此,我不得不直接将用户的创建与之关联

我如何保持SRP和这种验证机制

----更新----


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

我不确定我是否理解为什么首先需要一个专用的
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))

如果您有大量的
用户
特定验证逻辑,您不想污染
用户
对象,我想你可以把它分解成一个
UserValidator
trait,它可以扩展你的泛型
Validator
,并被
User
扩展。为什么我要处理组合而不是trait(继承),是因为我希望我的
UserValidator
是可模仿的。整个目标是提供一个
mockable
UserValidator
,以适合我关于
User
类的单元测试。当然,我们不能模拟一个精确类的任何特征/超类。我喜欢你的解决方案,尽管我认为保持组合,因此使用外部
UserValidator
类,但是让
User
类进行适当的不同验证方法调用,正如您所介绍的。非常感谢:)
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))