Я думаю, что вы правы, когда валидатор проверяет всю модель. Чтобы разорвать бесконечный цикл, вы можете передать значение в валидатор
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. Валидатор использует объект m_user, который предоставляет простые свойства без проверки, поэтому бесконечная рекурсия исчезает.
В настоящее время проверка выполняется при получении свойства. Я полагаю, что это сделано, потому что вы хотите проверить данные перед их использованием и избежать временных ошибок проверки при назначении свойств. В дополнение к проверке на уровне свойств вы можете также определить метод проверки «всего объекта», который проверяет все свойства вашего объекта, особенно те, которые связаны с ограничениями нескольких свойств. Например, если у вас есть ограничение A + B + C <50, то проверка AB и C как отдельных свойств приведет к тому, что это условие (A + B + C <50) будет оцениваться 3 раза, что не является необходимым, а также вводит в заблуждение так как ошибка появится в одном конкретном свойстве, тогда это действительно проблема со всеми 3 свойствами. Ваш валидатор уровня объекта может проверить это условие только один раз и отметить ошибку, которая указывает, что все 3 свойства недействительны. </p>
Все вышеперечисленное связывает Validation с классом User, так что клиенты могут использовать 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, поэтому будьте снисходительны к случайной синтаксической ошибке. Я программист ОО, поэтому я знаю, что принципы верны. И я только что заметил тег «asp-classic» - в некоторых примерах используются функции, которые могут быть недоступны в классическом asp, хотя отдельный код Validator - последний пример должен подойти для классического asp.