3

正如标题所述,我想知道我的验证类是否可以访问我模型中的所有属性。理想情况下,我想这样做,因为某些字段需要 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

如果我是对的并且这是一个好主意,我怎样才能用获取属性 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>

哪个生产解决方案图片

虽然不完美,但比我开始的要好得多。基本上,我决定使用装饰器模式。我的下一步很可能从每个验证中删除 Init() 函数,并将其替换为 SetModel() 函数或其他东西。这样每个验证都可以访问我模型中的每个属性。

谢谢大家。

4

2 回答 2

2

我通常定义一个验证器来验证整个模型;在这种情况下,我将有一个 UserValidator 类,该类具有一个接受用户并返回 ValidationResult 的方法,其中包括验证错误列表。

这允许您在不影响验证的情况下更改 User 类的实现(例如,您不必每次添加新属性时都向 Validator 类添加新方法,或者如果您想更改方法签名验证用户 ID 等)。

于 2010-05-28T17:57:26.623 回答
1

我认为您让验证器验证整个模型是正确的。要打破无限循环,您可以将值传递给验证器

Public Property Get UserID()
     UserID = m_Validator.IsUserIDValid(m_userID)
End property 

// in Validator
Public Function IsUserIDValid(userID)
    IsUserIDValid = userID > 13
End Function

或者,如果您更喜欢封装,您可以添加 Friend 函数来访问该属性而无需验证。

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

最后一种变体使用委托将基本用户属性与已验证的属性分开。我们让 User 成为一个抽象类,因为我们必须实现——一个有验证,一个没有。

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   

在最后一个示例中,ValidatedUser 看起来与您的原始代码相似,但关键区别在于 ValidatedUser 本身没有任何属性值 - 它将所有属性访问器委托给 m_User 对象。Validator 使用 m_user 对象,它提供简单的属性而无需验证,因此无限递归消失了。

目前,验证是在检索到属性时完成的。我想这样做是因为您想在使用数据之前对其进行验证,并避免在分配属性时出现瞬时验证错误。除了属性级别的验证之外,您可能还需要定义一个“整个对象”验证方法来检查对象上的所有属性,尤其是那些涉及多属性约束的属性。例如,如果您有约束 A+B+C < 50,那么将 AB 和 C 作为单独的属性进行检查将导致该条件 (A+B+C<50) 被评估 3 次,这是不必要的,而且也会造成混淆因为错误会出现在一个特定的属性上,当它确实是所有 3 个属性的问题时。

以上所有都将 Validation 绑定到 User 类,以便客户端可以使用 User 而无需担心验证。这种方法有优点也有缺点。好处是透明度 - 客户可以使用用户对象并在幕后获得验证,而无需明确要求。缺点是它将验证与您的模型紧密联系在一起。另一种方法是将验证与用户对象完全分开。这不仅解耦了验证,还提供了“整个对象”验证。例如

' 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

任何操作 User 的代码都必须在进行更改后显式调用 Validate,但这通常不是问题,而且控制级别比自动完成要好得多。(根据我的经验,您几乎总是不得不在某些时候撤消“自动”操作,因为它们会妨碍您。)

我写的超出了我的预期。我希望这是有帮助的!

PS:我不怎么做VB,所以请原谅偶尔的语法错误。我是一个面向对象的程序员,所以我知道这些原则是正确的。而且我刚刚注意到“asp-classic”标签 - 一些示例使用了经典 asp 中可能不可用的功能,尽管单独的验证器代码 - 最后一个示例在经典 asp 上应该没问题。

于 2010-06-01T21:30:39.777 回答