[英]Cohesive way to validate a class in Scala using Scalaz 7
My goal is to validate User
's fields within the object
's apply
method before creating one effective User
instance: 我的目标是在创建一个有效的
User
实例之前验证object
的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.
}
}
I chose to use Validation
from Scalaz7 to accumulate potential illegal arguments / errors. 我选择使用Scalaz7中的
Validation
来累积潜在的非法参数/错误。
One drawback in the following code is that Scalaz7 API force me to make the validator creates itself the instance. 以下代码的一个缺点是Scalaz7 API迫使我使验证器自己创建实例。 However, by following Single-Responsibility principle , it's clearly not its role.
但是,遵循单一责任原则 ,显然不是它的作用。 Its role would be to just validate fields and to return some errors list.
它的作用是仅验证字段并返回一些错误列表。
Let's first present my actual code (for information, Empty****
objects are just some case object
extending UserCreationFailure
): 让我们首先介绍我的实际代码(有关信息,
Empty****
对象只是扩展UserCreationFailure
一些case object
):
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
}
}
}
What I would expect is to merely return this snippet code: 我期望的是只返回这段代码:
(checkForUserName ⊛ checkForPassword)
and bring the appropriate result into my User
class, allowing to create the effective instance by doing: 并将适当的结果带入我的
User
类,允许通过执行以下操作来创建有效的实例:
def apply(userValidator: UserValidator): ValidationNel[UserCreationFailure, User] = {
userValidator(username, password).validate()((userName, password)(new User(userName, password))
}
Indeed, it would be more friendly with SRP. 实际上,SRP会更友好。
But (checkForUserName ⊛ checkForPassword)
returns a totally private
type type: 但是
(checkForUserName ⊛ checkForPassword)
返回一个完全private
类型:
private[scalaz] trait ApplicativeBuilder[M[_], A, B]
, private[scalaz] trait ApplicativeBuilder[M[_], A, B]
,
thus I don't have the hand on the type of class
returned. 因此我没有关于返回
class
的类型。
Therefore, I am forced to directly associate User's creation with it. 因此,我被迫直接将用户的创建与其关联。
How could I keep SRP and keep this validation mechanism? 我怎样才能保留SRP并保持这种验证机制?
-----UPDATE ---- -----更新 ----
As @Travis Brown mentioned, the intent to use an external class
for my UserValidator
may seem weird. 正如@Travis Brown所提到的,为
UserValidator
使用外部class
的意图可能看起来很奇怪。 Actually, I expect the validator to be mockable and thus, I'm forced to use composition over trait
/ abstract class
. 实际上,我希望验证器是可模拟的,因此,我被迫使用组合优于
trait
/ abstract class
。
I'm not sure I understand why you need a dedicated UserValidator
class in the first place. 我不确定我是否理解为什么你需要一个专门的
UserValidator
类。 In a case like this I'd be more likely to bundle all of my generic validation code into a separate trait, and to have my User
companion object (or whatever other piece I want to be responsible for creating User
instances) extend that trait. 在这样的情况下,我更有可能将我的所有通用验证代码捆绑到一个单独的特征中,并让我的
User
伴侣对象(或者我想要负责创建User
实例的任何其他部分)扩展该特征。 Here's a quick sketch: 这是一个快速草图:
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)
}
And then: 然后:
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))
If you have a huge amount of User
-specific validation logic that you don't want polluting your User
object, I suppose you could factor it out into a UserValidator
trait that would extend your generic Validator
and be extended by User
. 如果您有大量
User
特定的验证逻辑,您不希望污染您的User
对象,我想您可以将其分解为UserValidator
特征,该特征将扩展您的通用Validator
并由User
扩展。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.