我的目标是在创建一个有效的用户之前验证
实例:对象
的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****
对象只是一些扩展UserCreationFailure
的case 对象
):
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/