2013-05-05 25 views
3

验证Scala中的一个类凝聚力的方式我的目标是建立一个有效的User实例之前验证小号apply方法User“的object内S场”:使用Scalaz 7

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

} 

我选择使用ValidationScalaz7积累潜在的非法论点/错误。

以下代码的一个缺点是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并保持此验证机制?

----- UPDATE ----

由于@Travis布朗提到的,意图使用外部classUserValidator可能看起来怪怪的。实际上,我希望验证器是可嘲弄的,因此,我不得不使用trait/abstract class的构图。

回答

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)) 

如果你有一个巨大的,你不希望你的污染对象UserUser特异性验证逻辑的量,我想你可以因素它会扩展到UserValidator特征,这将扩展您的通用Validator并扩展User

+0

为什么我想处理组成而不是特质(继承)是因为我期望我的'UserValidator'是可嘲弄的。整个目标是提供一个'可嘲讽的'UserValidator',以便适合我的'User'类的单元测试。 – Mik378 2013-05-05 20:39:23

+0

当然,我们不能嘲笑任何一个预定班的任何特质/超类。 – Mik378 2013-05-05 20:51:00

+0

我喜欢你的解决方案,尽管我认为保持作文,因此与外部'UserValidator'类,但让'用户'类进行适当的不同验证方法调用,就像你已经提出的。非常感谢 :) – Mik378 2013-05-05 21:39:57