2013-05-05 24 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