2010-05-28 1 views
3

Как говорится в заголовке, мне интересно, действительно ли моя класс проверки имеет доступ ко всем свойствам моей модели. В идеале я хотел бы это сделать, потому что для некоторых полей требуется еще 10 других полей, чтобы проверить, действительно ли это или нет. I мог бы, но скорее не имел бы функций с 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> 

Который производит Solution image http://i46.tinypic.com/zsvk9z.png

Пока не совершенны, это лучше, чем то, что я начал с. В принципе, я решил использовать узор декоратора. Мой следующий шаг - скорее всего, удалить функцию Init() из каждой проверки и заменить ее на функцию SetModel() или что-то в этом роде. Таким образом, каждый валидатор может иметь доступ ко всем свойствам моей модели.

Спасибо всем.

ответ

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 

Окончательный вариант использует делегирование сохранить основные свойства пользователя отделиться от проверенных единиц. Мы делаем Пользователя абстрактным классом, поскольку у нас есть реализации - одно с проверкой, а одно без.

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 не имеет значения свойств - он делегирует все Accessors собственности на объект m_User. Валидатор использует объект m_user, который предоставляет простые свойства без проверки, поэтому бесконечная рекурсия уходит.

В настоящее время валидация выполняется при возврате имущества. Я предполагаю, что это сделано, потому что вы хотите проверить данные до их использования и избежать ошибок при проверке, связанных с изменением свойств. В дополнение к проверке уровня собственности вы можете также определить метод проверки «всего объекта», который проверяет все свойства вашего объекта, особенно те, которые связаны с ограничениями, связанными с несколькими свойствами. Например, если у вас есть ограничение A + B + C < 50, то проверка AB и C как отдельных свойств приведет к тому, что условие (A + B + C < 50) оценивается 3 раза, что необязательно, а также запутывает так как ошибка появится в одном конкретном свойстве, когда это действительно проблема со всеми 3 свойствами. Ваш валидатор уровня объекта может проверить это условие только один раз и указать ошибку, которая указывает, что все 3 свойства недействительны.

Все вышеперечисленное связывает проверку с классом User, чтобы клиенты могли использовать Пользователя без заботы о валидации. При таком подходе есть преимущества и недостатки. Преимущество прозрачности - клиент может использовать объекты 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 

Любой код манипулирует пользователь будет иметь явно вызвать Validate после внесения изменений, но это, как правило, не является проблемой, и уровень контроля гораздо лучше, чем это делается автоматически. (По моему опыту, вы почти всегда должны отменить «автоматические» действия в какой-то момент, потому что они мешают.)

Я написал больше, чем планировал. Надеюсь, это полезно!

PS: Я не делаю много VB, поэтому, пожалуйста, будьте снисходительны к случайной синтаксической ошибке. Я программист OO, поэтому я знаю, что принципы верны. И я просто заметил тег asp-classic. В некоторых примерах используются функции, которые могут быть недоступны в классическом asp, хотя отдельный код Validator - последний пример, должен быть в порядке на классическом asp.

+0

Woah! Этот пост очень ценится, спасибо. :) Хотя мое окончательное решение не на 100%, что вы разместили. Это довольно близко. – Mike

2

Обычно я определяю валидатор, который проверяет всю модель; В этом случае у меня будет класс UserValidator, у которого есть метод, который принимает пользователя и возвращает ValidationResult, который включает в себя список ошибок проверки.

Это позволяет изменить реализацию класса пользователя, не влияя на проверку (например, вам не нужно добавлять новый метод в класс Validator при каждом добавлении нового свойства или изменять подпись метода, если вы хотите изменить способ проверки UserID и т. д.).

+0

Не могли бы вы привести пример? – Mike