我的验证者应该可以访问我的整个模型吗?
-
05-10-2019 - |
题
正如标题所述,我想知道我的验证类可以从模型中访问所有属性是否是个好主意。理想情况下,我想这样做,因为某些字段需要10个以上的其他字段来验证其是否有效。我 可以 但宁愿没有10+参数的函数。还是这会使模型和验证器彼此相互结合?这是我的意思的一个小例子。但是,此代码不起作用,因为它给出了无限循环!
Class User
Private m_UserID
Private m_Validator
Public Sub Class_Initialize()
End Sub
Public Property Let Validator(value)
Set m_Validator = value
m_Validator.Initialize(Me)
End Property
Public Property Get Validator()
Validator = m_Validator
End Property
Public Property Let UserID(value)
m_UserID = value
End property
Public Property Get UserID()
UserID = m_Validator.IsUserIDValid()
End property End Class
Class Validator
Private m_User
Public Sub Class_Initialize()
End Sub
Public Sub Initialize(value)
Set m_User = value
End Sub
Public Function IsUserIDValid()
IsUserIDValid = m_User.UserID > 13
End Function End Class
Dim mike : Set mike = New User
mike.UserID = 123456 mike.Validator = New Validator
Response.Write mike.UserID
如果我是对的,这是一个好主意,我该如何使用Get Property UserId来修复无限循环?
谢谢你。
解决方案
<!-- #include file = "../lib/Collection.asp" -->
<style type="text/css">
td { padding: 4px; }
td.error
{
background: #F00F00;
}
td.warning
{
background: #FC0;
}
</style>
<%
Class UserModel
Private m_Name
Private m_Age
Private m_Height
Public Property Let Name(value)
m_Name = value
End Property
Public Property Get Name()
Name = m_Name
End Property
Public Property Let Age(value)
m_Age = value
End Property
Public Property Get Age()
Age = m_Age
End Property
Public Property Let Height(value)
m_Height = value
End Property
Public Property Get Height()
Height = m_Height
End Property
End Class
Class NameValidation
Private m_Name
Public Function Init(name)
m_Name = name
End Function
Public Function Validate()
Dim validationObject
If Len(m_Name) < 5 Then
Set validationObject = New ValidationError
Else
Set validationObject = New ValidationSuccess
End If
validationObject.CellValue = m_Name
Set Validate = validationObject
End Function
End Class
Class AgeValidation
Private m_Age
Public Function Init(age)
m_Age = age
End Function
Public Function Validate()
Dim validationObject
If m_Age < 18 Then
Set validationObject = New ValidationError
ElseIf m_Age = 18 Then
Set validationObject = New ValidationWarning
Else
Set validationObject = New ValidationSuccess
End If
validationObject.CellValue = m_Age
Set Validate = validationObject
End Function
End Class
Class HeightValidation
Private m_Height
Public Function Init(height)
m_Height = height
End Function
Public Function Validate()
Dim validationObject
If m_Height > 400 Then
Set validationObject = New ValidationError
ElseIf m_Height = 324 Then
Set validationObject = New ValidationWarning
Else
Set validationObject = New ValidationSuccess
End If
validationObject.CellValue = m_Height
Set Validate = validationObject
End Function
End Class
Class ValidationError
Private m_CSSClass
Private m_CellValue
Public Property Get CSSClass()
CSSClass = "error"
End Property
Public Property Let CellValue(value)
m_CellValue = value
End Property
Public Property Get CellValue()
CellValue = m_CellValue
End Property
End Class
Class ValidationWarning
Private m_CSSClass
Private m_CellValue
Public Property Get CSSClass()
CSSClass = "warning"
End Property
Public Property Let CellValue(value)
m_CellValue = value
End Property
Public Property Get CellValue()
CellValue = m_CellValue
End Property
End Class
Class ValidationSuccess
Private m_CSSClass
Private m_CellValue
Public Property Get CSSClass()
CSSClass = ""
End Property
Public Property Let CellValue(value)
m_CellValue = value
End Property
Public Property Get CellValue()
CellValue = m_CellValue
End Property
End Class
Class ModelValidator
Public Function ValidateModel(model)
Dim modelValidation : Set modelValidation = New CollectionClass
' Validate name
Dim name : Set name = New NameValidation
name.Init model.Name
modelValidation.Add name
' Validate age
Dim age : Set age = New AgeValidation
age.Init model.Age
modelValidation.Add age
' Validate height
Dim height : Set height = New HeightValidation
height.Init model.Height
modelValidation.Add height
Dim validatedProperties : Set validatedProperties = New CollectionClass
Dim modelVal
For Each modelVal In modelValidation.Items()
validatedProperties.Add modelVal.Validate()
Next
Set ValidateModel = validatedProperties
End Function
End Class
Dim modelCollection : Set modelCollection = New CollectionClass
Dim user1 : Set user1 = New UserModel
user1.Name = "Mike"
user1.Age = 12
user1.Height = 32
modelCollection.Add user1
Dim user2 : Set user2 = New UserModel
user2.Name = "Phil"
user2.Age = 18
user2.Height = 432
modelCollection.Add user2
Dim user3 : Set user3 = New UserModel
user3.Name = "Michele"
user3.Age = 32
user3.Height = 324
modelCollection.Add user3
' Validate all models in the collection
Dim modelValue
Dim validatedModels : Set validatedModels = New CollectionClass
For Each modelValue In modelCollection.Items()
Dim objModelValidator : Set objModelValidator = New ModelValidator
validatedModels.Add objModelValidator.ValidateModel(modelValue)
Next
%>
<table>
<tr>
<td>Name</td>
<td>Age</td>
<td>Height</td>
</tr>
<%
Dim r, c
For Each r In validatedModels.Items()
%><tr><%
For Each c In r.Items()
%><td class="<%= c.CSSClass %>"><%= c.CellValue %></td><%
Next
%></tr><%
Next
%>
</table>
虽然不是完美的,但它比我最初的东西要好。基本上,我决定使用装饰器图案。我的下一步是很可能从每个validate中删除init()函数,然后用setModel()函数或其他内容替换它。这样,每个验证都可以访问我的模型中的每个属性。
谢谢大家。
解决方案
我认为您是正确的,使验证器验证整个模型。要打破无限循环,您可以将值传递给验证器
Public Property Get UserID()
UserID = m_Validator.IsUserIDValid(m_userID)
End property
// in Validator
Public Function IsUserIDValid(userID)
IsUserIDValid = userID > 13
End Function
另外,如果您喜欢封装,则可以添加朋友功能以无需验证即可访问该属性。
Public Property Get UserID()
UserID = m_Validator.IsUserIDValid()
End property
Friend Function GetUserID()
GetUserID = m_userID
End Function
// in Validator
Public Function IsUserIDValid()
// "private" access - to get the unvalidated property
IsUserIDValid = m_user.GetUserID > 13
End Function
第三种方法是将对象与验证分开。基类无需验证定义所有适当的人。然后,您定义一个添加验证的子类:
class User
Private m_userID
Public Property Get UserID()
UserID = m_userID
End property
End Class
class ValidatedUser inherits User
Public Overrides Property Get UserID()
if (m_userID<15)
// handle invalid case, e.g. throw exception with property that is invalid
UserID = m_userID
End Property
Public Function Validate()
' class-level validation
End Function
End Class
最终变化使用委托将基本用户属性与经过验证的用户属性分开。我们使用户成为抽象类,因为我们必须实现 - 一个具有验证的类别,而没有实现。
Class MustInherit User
Public MustInherit Property Get UserID()
End Class
' A simple implementation of User that provides the properties
Class DefaultUser Inherits User
Private m_UserID
Public Overrides Property Get UserID()
UserID = m_UserID
End Property
End Class
Class ValidatedUser Inherits User
private Validator m_validator
private User m_User
Public Property Let Validator(value)
Set m_Validator = value
m_Validator.Initialize(m_User)
' note that validator uses m_User - this breaks the infinite recursion
End Property
Public Overrides Property Let UserID(value)
m_User.UserID = value;
End Property
Public Overrides Property Get UserID()
UserID = m_validator.IsUserValid();
End Property
End Class
在最后一个示例中,验证的用户看起来与您的原始代码相似,但是关键区别在于验证的使用者本身没有任何属性值 - 它将所有属性访问者委派给M_USER对象。验证器使用M_USER对象,该对象提供没有验证的简单属性,因此无限递归消失了。
目前,在检索属性时进行验证。我想这是因为您想在使用数据之前验证数据,并在分配属性时避免瞬态验证错误。除了属性级别的验证外,您可能还需要定义一个“整个对象”验证方法,该方法检查对象上的所有属性,尤其是涉及多专业约束的属性。例如,如果您具有约束A+B+C <50,则将AB和C作为单独的属性进行检查将导致该条件(A+B+C <50)进行了3次评估,这是不必要的,并且也令人困惑由于该错误将出现在一个特定属性上,因此这确实是所有3个属性的问题。您的对象级验证器只能检查一次条件,并标记一个指示所有3个属性无效的错误。
以上所有这些都将验证绑定到用户类,以便客户可以在不关心验证的情况下使用用户。这种方法有好处和缺点。好处是透明度 - 客户端可以使用用户对象并在不明确要求的情况下获得幕后验证。缺点是它与您的模型非常紧密地联系在一起。一种替代方法是将验证与用户对象完全分开。这不仅取消了验证,而且还提供了“全体对象”验证。例如
' User is now a simple class (like DefaultUser above '
' with just properties, no validation '
Class UserValidator
Public Function Validate(user)
' validate the given user object, return a list of
' validation errors, each validation error object
' that describes the property or properties
' that caused the validation error and why it's an error
' E.g. '
Dim ve As ValidationError
ve = new ValidationError
ve.obj = user; ' the object that failed validation
ve.property = "userID"
ve.msg = "userId must be < 15"
' potentially put several of these in a list and return to caller
End
End Class
然后,任何操纵用户的代码都必须在进行更改后明确调用验证,但这通常不是问题,并且控制级别比自动完成的控制水平要好得多。 (根据我的经验,您几乎总是必须在某个时候撤消“自动”动作,因为它们会妨碍您。)
我写的比预期的要多。我希望这是有帮助的!
PS:我没有做太多的VB,所以请宽容偶尔的语法错误。我是OO程序员,所以我知道原理是正确的。而且我只是注意到“ ASP经典”标签 - 一些示例使用了经典ASP中可能无法使用的功能,尽管单独的验证器代码 - 最后一个示例应该在经典ASP上很好。
其他提示
我通常定义验证整个模型的验证器;在这种情况下,我将拥有一个Uservalidator类,该类具有接受用户并返回验证的方法,其中包括验证错误列表。
这使您可以在不影响验证的情况下更改用户类的实现(例如,每次添加新属性时,您都不必在验证器类中添加新方法,或者如果要更改如何更改方法签名用户ID已验证等)。