Опять валидация данных
14.03.2005
|
IT |
Здравствуйте, Козьма Прутков, Вы писали:
КП>Если не жалко, поделитесь идеями, как организовать подобную функциональность...
Прежде всего я бы рассмотрел различные стадии валидации и почему они необходимы, а уже потом её реализацию.
К сожалению, валидацию не получается делать в одном месте за один раз по ряду причин, поэтому обычно она разбивается на несколько уровней.
1. Базовая валидация допустимых параметров вводимых значений. Сюда входит проверка обязательности и длин полей, формат вводимых данных. Основное назначение данного уровня — минимизировать возможность неправильного ввода, исключить походы на сервер (в случае Web) и предоставить пользователю информацию о некорректно-введённых значениях.
2. Проверка валидности бизнес-энтити как единого целого. Этот уровень повторяет все проверки предыдущего, плюс добавляет проверки комбинации полей объекта. Например, если вводимый адрес принадлежит US, то Zip код должен быть 5 или 9 символов, если это Россия, то размер почтового индекса должен равняться 6-ти.
3. Проверка валидности объекта в пределах системы. Включает все предыдущие проверки, плюс всевозможные проверки на непротиворечивость объекта в системе. Например, тот же адрес обычно проверятся на существование такового с помощью специализированных систем. Email пользователя не должен дублироваться в системе и т.п.
4. Проверка целостности данных перед их сохранением в базе данных. Обычно с успехом выполняется средствами самой БД.
Теперь о реализации. С (4) всё понятно. Использвание констрейнов, пользовательских типов, уникальных индексов и FK решают эту проблему. Частично, например, в случае с e-mail, сюда можно перенести проверки 3-го уровня. В случае обычного UI (не-Web) приложения обычно удаётся совместить 1-й и 2-й уровни. Для Web-приложений 1-й (обычно не полностью) выполняется в браузере, 2-й и 3-й уровни можно совместить, если не используется выделенный сервер приложений. Если используется, то 2-й в Web-приложении, 3-й в сервере приложений.
Реализация 2-го и 3-го уровня обычно делается ручками. Ничего здесь автоматизировать, к сожалению, не получается. Причём 2-й следует делать как часть самого бизнес-объекта, а 3-й как метод бизнес логики. А вот с 1-м, который как правило самый занудный всё гораздо интереснее. Лучшая, на мой взгляд, реализация данного уровня — это применение атрибутов (речь, конечно идёт о .NET). В этом случае, т.к. мы фактически расширяем метадату объекта, удаётся по большей части автоматизировать проверки на UI, т.е. не только свести саму реализацию валидации объекта к описанию в виде атрибутов, но и натренировать UI компоненты понимать эти атрибуты.
Таким образом, 1-й и 2-й уровень удаётся инкапсулировать в самом объекте. 3-й уровень представляет собой один метод бизнес логики. 4-й реализуется средствами самой БД.
В общем, как-то так
КП>Если не жалко, поделитесь идеями, как организовать подобную функциональность...
Прежде всего я бы рассмотрел различные стадии валидации и почему они необходимы, а уже потом её реализацию.
К сожалению, валидацию не получается делать в одном месте за один раз по ряду причин, поэтому обычно она разбивается на несколько уровней.
1. Базовая валидация допустимых параметров вводимых значений. Сюда входит проверка обязательности и длин полей, формат вводимых данных. Основное назначение данного уровня — минимизировать возможность неправильного ввода, исключить походы на сервер (в случае Web) и предоставить пользователю информацию о некорректно-введённых значениях.
2. Проверка валидности бизнес-энтити как единого целого. Этот уровень повторяет все проверки предыдущего, плюс добавляет проверки комбинации полей объекта. Например, если вводимый адрес принадлежит US, то Zip код должен быть 5 или 9 символов, если это Россия, то размер почтового индекса должен равняться 6-ти.
3. Проверка валидности объекта в пределах системы. Включает все предыдущие проверки, плюс всевозможные проверки на непротиворечивость объекта в системе. Например, тот же адрес обычно проверятся на существование такового с помощью специализированных систем. Email пользователя не должен дублироваться в системе и т.п.
4. Проверка целостности данных перед их сохранением в базе данных. Обычно с успехом выполняется средствами самой БД.
Теперь о реализации. С (4) всё понятно. Использвание констрейнов, пользовательских типов, уникальных индексов и FK решают эту проблему. Частично, например, в случае с e-mail, сюда можно перенести проверки 3-го уровня. В случае обычного UI (не-Web) приложения обычно удаётся совместить 1-й и 2-й уровни. Для Web-приложений 1-й (обычно не полностью) выполняется в браузере, 2-й и 3-й уровни можно совместить, если не используется выделенный сервер приложений. Если используется, то 2-й в Web-приложении, 3-й в сервере приложений.
Реализация 2-го и 3-го уровня обычно делается ручками. Ничего здесь автоматизировать, к сожалению, не получается. Причём 2-й следует делать как часть самого бизнес-объекта, а 3-й как метод бизнес логики. А вот с 1-м, который как правило самый занудный всё гораздо интереснее. Лучшая, на мой взгляд, реализация данного уровня — это применение атрибутов (речь, конечно идёт о .NET). В этом случае, т.к. мы фактически расширяем метадату объекта, удаётся по большей части автоматизировать проверки на UI, т.е. не только свести саму реализацию валидации объекта к описанию в виде атрибутов, но и натренировать UI компоненты понимать эти атрибуты.
Таким образом, 1-й и 2-й уровень удаётся инкапсулировать в самом объекте. 3-й уровень представляет собой один метод бизнес логики. 4-й реализуется средствами самой БД.
В общем, как-то так
... << RSDN@Home 1.1.4 beta 3 rev. 185>>
14.03.2005 123 комментария |
IT>К сожалению, валидацию не получается делать в одном месте за один раз по ряду причин, поэтому обычно она разбивается на несколько уровней.
Отлично. Ясно и по полочкам.
Да, с форматами и длинами я пожалуй спорить не буду: такие вещи крайне редко меняются, особенно форматы .
Рассмотрим веб-приложение (ибо сложнее), но аналогично и винформовое. Пусть по первоначальным требованиям поле Заказчик обязательное. Ок, сделали. Через 3 месяца оказывается, что оно не является обязательным. И мы шагом марш по всем уровням валидации это дело править. Или я чего-то не понял?
Таперь рассмотрим винформс-приложение (в веб-приложении решается написанием метода установки пары значений). Вот подвязана у нас к свойствам Country и ZipCode (он же индекс для примера) пара контролов. По идее, если пользователь меняет один из них, то нужно проверить их пару. Так что пользователь, однажды введя US/12345 уже никогда не введет RU/131000, ибо перекрестные комбинации невалидны. Ну, либо позволить перевести БО в неконсистентное состояние, что вряд ли очень хорошо.
КП>Рассмотрим веб-приложение (ибо сложнее), но аналогично и винформовое. Пусть по первоначальным требованиям поле Заказчик обязательное. Ок, сделали. Через 3 месяца оказывается, что оно не является обязательным. И мы шагом марш по всем уровням валидации это дело править. Или я чего-то не понял?
КП>Таперь рассмотрим винформс-приложение (в веб-приложении решается написанием метода установки пары значений). Вот подвязана у нас к свойствам Country и ZipCode (он же индекс для примера) пара контролов. По идее, если пользователь меняет один из них, то нужно проверить их пару. Так что пользователь, однажды введя US/12345 уже никогда не введет RU/131000, ибо перекрестные комбинации невалидны. Ну, либо позволить перевести БО в неконсистентное состояние, что вряд ли очень хорошо.
Ты говоришь о валидации непосредственно во время ввода, о валидации каждого введённого символа. Мне эта практика кажется крайне порочной как раз по причинам, которые ты описываешь. Формы, которые думают что они умней меня всегда раздражают. Валидация значений и их комбинаций должна делаться при сабмите формы. Во время ввода можно проверять только формат, типа ничего не давать пользователю вводить кроме цифр и т.п.
Что же касается первого вопроса об изменениях по всем уровням валидации, то здесь я видимо не совсем ясно выразился. Говоря "этот уровень повторяет все проверки предыдущего" я не имел ввиду дублирование кода, я имел ввиду его повторное использование. Например, имеем вот такой класс Address:
Здесь в одном месте реализованы 1-й и 2-й уровни валидации. Третий в бизнес объекте:
4-й в БД.
Теперь, допустим, нам нужно одно из полей сделать необязательным. Сколько нужно сделать изменений? Правильно, две. Первое — убрать Required атрибут в классе, второе — NOT NULL в SQL скрипте. Готовоо.
IT>Ты говоришь о валидации непосредственно во время ввода, о валидации каждого введённого символа. Мне эта практика кажется крайне порочной как раз по причинам, которые ты описываешь. Формы, которые думают что они умней меня всегда раздражают. Валидация значений и их комбинаций должна делаться при сабмите формы. Во время ввода можно проверять только формат, типа ничего не давать пользователю вводить кроме цифр и т.п.
Боже упаси! Как ты мог так плохо обо мне подумать! Просто насколько я помню в винформах при подвязке свойств к контролам "сбрасывание" значений в свойства происходит по одному, соответственно, либо страна либо индекс будет присвоен первым, и проверить это нельзя. Но это бог с ним, издержки технологии...
IT>Что же касается первого вопроса об изменениях по всем уровням валидации, то здесь я видимо не совсем ясно выразился. Говоря "этот уровень повторяет все проверки предыдущего" я не имел ввиду дублирование кода, я имел ввиду его повторное использование. Например, имеем вот такой класс Address:
<skipped />
IT>Теперь, допустим, нам нужно одно из полей сделать необязательным. Сколько нужно сделать изменений? Правильно, две. Первое — убрать Required атрибут в классе, второе — NOT NULL в SQL скрипте. Готовоо.
Класс. Но тут как раз и происходит та проблема, о которой я говорил: ты можешь перевести объект в неконсистентное состояние. То есть легко сунуть ему новую страну, а потом сразу в БД.
Ладно, я приблизительно понял твою мысль. Спасибо.
КП>Класс. Но тут как раз и происходит та проблема, о которой я говорил: ты можешь перевести объект в неконсистентное состояние. То есть легко сунуть ему новую страну, а потом сразу в БД.
Сразу в бд неполучится, т.к. насколько я понимаю AddressService.AddAddress должен быть единтвенным доступным способом сохранить объект, а он делает валидацию.
Но проблему это все равно не решает, так как с объектом еще необходимо работать и программно (из бинзес логики). И когда я передаю такой объект, например, параметром в какую-нибудь функцию, то я хочу, чтобы он был в валидном состоянии. А проверять каждый раз — это лучше сразу застрелиться.
Поэтому лучше, чтобы объект был написан таким образом, чтобы не было возможности перевести его в невалидное состояние. Ведь именно для этого и придумана инкапсуляция и свойства с сеттерами, не так ли?
Таким образом получается, что для работы с формами лучше иметь бизнес объекты с:
— наличием конструктора с определенной сигнатурой, например, конструктора без параметров, чтобы форма (или другой сервис) могла без проблем создавать новые объекты
— полями (без сеттеров), чтобы форма могла присваивать значения в любой последовательности
— и функцией validate, которая вызывается формой или другими сервисами автоматически.
А для программной работы лучше иметь визнес объекты, написанные по всем правилам ООП:
— с такими конструкторами, какие необходимы логически
— со свойствами
— и т.д.
Получается противочечие .
Я вот пишу движок для всего этого (времени много, начальство одобряет). Есть свой язык, своя VM. И если представить, что можно реализовать любые экзотические конструкции в языке, специально для объявления и работы с бизнес объектами, какие-бы вы предпочли? Так, чтобы удобно было и для форм и для бизнес логики? Есть идеи? .
S>Получается противочечие .
Никакого противоречия нет. На разных уровнях разная бизнес-логика. И поэтому объект проходя по уровню либо меняет свой вид, или предлагает интерфейс, которым могут воспользоваться объекты бизнес логики нескольких уровней. Поэтому бизнес-объект и не делают сверх-навороченным.
S>Я вот пишу движок для всего этого (времени много, начальство одобряет). Есть свой язык, своя VM. И если представить, что можно реализовать любые экзотические конструкции в языке, специально для объявления и работы с бизнес объектами, какие-бы вы предпочли? Так, чтобы удобно было и для форм и для бизнес логики? Есть идеи? .
Бизнес-правила разные для форм и для бизнес-логики. Игорь хорошо это описал. Даже если ты описываешь операцию валидирования, многие операции можно выполнить только на 3 и 4 уровне. Если, конечно, тебе не хватает функциональности System.Data.DataSet.
С уважением, Gleb.
GZ>Никакого противоречия нет. На разных уровнях разная бизнес-логика. И поэтому объект проходя по уровню либо меняет свой вид, или предлагает интерфейс, которым могут воспользоваться объекты бизнес логики нескольких уровней. Поэтому бизнес-объект и не делают сверх-навороченным.
Т.е. объект с "изменяющимся видом" и кучей интерфейсов не считается навороченным? И с ним, наверное, очень просто работать (интуитивно понятно, так сказать).
А потом, я всегда рассуждаю так: есть объект и есть остальная система, которая им пользуется. Так что функциональность разделяется. Но, ту функциональность, которая на объект возложена, он должен выполнять качественно, т.е. не давать перевести себя в невалидное состояние (невалидное, с точки зрения самого объекта).
Например, выше описанный Address:
Его смысл состоит в представлении информации о адресе, исходя из этого он (объект) и должен делать валидацию себя. Одной подсистеме, использующей данный объект может и не нужно знать существует ли такой адрес в реальности, а другой — нужно, исходя из этого подсистема делает (или не делает) соответствующую проверку.
И основная мысль в том, что эта проверка никоим образом к бизнес объекту не относится, она относится к способу использования этого бизнес объекта в конкретной подсистеме.
Поэтому получаются две совершенно разные валидации:
— валидация самого объекта
— валидация состояния системы по завершению бизнес транзакции
Причем вторая не столь формализуема как первая.
GZ>Бизнес-правила разные для форм и для бизнес-логики.
Но есть бизнес объект с которым работает и форма и бизнес логика. И валидация с точки зрения самого объекта должна быть одна и та же.
GZ>Даже если ты описываешь операцию валидирования, многие операции можно выполнить только на 3 и 4 уровне.
На третьем — да, об этом я написал выше в данном посте, но повторюсь, что это не валидирование объекта, а валидирование способа его использования в како-либо подсистеме.
Четвертый уровень — только физический. Никакой дополнительной логической нагрузки он не несет. В нормальном фреймворке с базой напрямую никто и работать-то не должен, только с бизнес объектами и специальным языком запросов. А то, что в реальности некоторые проверки удобно выполнять в СУБД (ссылочную целостность, например), так это только оптимизация (ну и еще успокоение нервов админа СУБД), их принципиально можно делать и сервере приложений.
GZ>Если, конечно, тебе не хватает функциональности System.Data.DataSet.
Вот люди. Я с ними как с художниками, про новый движок, про специальный язык, а они... Ширее надо мыслить .
S>Здравствуйте, GlebZ, Вы писали:
GZ>>Никакого противоречия нет. На разных уровнях разная бизнес-логика. И поэтому объект проходя по уровню либо меняет свой вид, или предлагает интерфейс, которым могут воспользоваться объекты бизнес логики нескольких уровней. Поэтому бизнес-объект и не делают сверх-навороченным.
S>Т.е. объект с "изменяющимся видом" и кучей интерфейсов не считается навороченным? И с ним, наверное, очень просто работать (интуитивно понятно, так сказать).
Допустим, у тебя есть набор полей в форме, которое переводится в бизнес-объект с клиентской бизнес-логикой, которое переводится в некоторый объект DTO, которое переводится в бизнес-объект доменной модели, которое в свою очередь переводится в набор строк в базе данных. Никакой кучи интерфейсов. Просто состояние объекта находится в том виде, которое нужно в данный момент. Естественно данный список приведен от балды.
S>А потом, я всегда рассуждаю так: есть объект и есть остальная система, которая им пользуется. Так что функциональность разделяется. Но, ту функциональность, которая на объект возложена, он должен выполнять качественно, т.е. не давать перевести себя в невалидное состояние (невалидное, с точки зрения самого объекта).
Бизнес-логика которая на клиенте, и бизнес-логика которая на сервере(пускай это даже сервер БД) разные. У них не просто разные возможности, но и разные цели.
S>Например, выше описанный Address:
S>Его смысл состоит в представлении информации о адресе, исходя из этого он (объект) и должен делать валидацию себя. Одной подсистеме, использующей данный объект может и не нужно знать существует ли такой адрес в реальности, а другой — нужно, исходя из этого подсистема делает (или не делает) соответствующую проверку.
S>И основная мысль в том, что эта проверка никоим образом к бизнес объекту не относится, она относится к способу использования этого бизнес объекта в конкретной подсистеме.
S>Поэтому получаются две совершенно разные валидации:
S>
S>- валидация самого объекта
S>- валидация состояния системы по завершению бизнес транзакции
S>
S>Причем вторая не столь формализуема как первая.
Давай классифицируем когда происходит валидация данных на форме. Есть 3 случая:
1. При вводе каждого символа. Например, бизнес задачей определено что в данном поле может быть только цифра. Тогда поле не должно воспринимать ничего кроме нажатие на клавиши с цифрами.
2. При потере фокуса. Если неверное значение, то фокус не должен уходить
3. Перед отправлением объекта на другой уровень.
1-2 случаи логикой бизнес-объекта не управляются. В случае создания нового бизнес-объекта, его может еще и не сущестовать. Плюс к этому, значения выбранные из справочника — вообще незачем на клиенте валидировать.
Более криминальный случай, например:
у тебя объект должен показываться иконкой в зависимости от значения. И на разных формах(клиентах) показ поля разный. Так что запихнуть всю бизнес логику на бизнес-объект, даже на уровне клиента не удастся.
GZ>>Бизнес-правила разные для форм и для бизнес-логики.
S>Но есть бизнес объект с которым работает и форма и бизнес логика. И валидация с точки зрения самого объекта должна быть одна и та же.
Частично да. Но например не обязательно объекты бизнес логики должны пользоваться функционалом бизнес-объекта. В случае уровня 3, легче проверять объект от а до я(или от a to z) полностью. Особенно в случае доменной модели. На 4 уровне — это может быть обязательным условием.
GZ>>Даже если ты описываешь операцию валидирования, многие операции можно выполнить только на 3 и 4 уровне.
S>На третьем — да, об этом я написал выше в данном посте, но повторюсь, что это не валидирование объекта, а валидирование способа его использования в како-либо подсистеме.
S>Четвертый уровень — только физический. Никакой дополнительной логической нагрузки он не несет. В нормальном фреймворке с базой напрямую никто и работать-то не должен, только с бизнес объектами и специальным языком запросов. А то, что в реальности некоторые проверки удобно выполнять в СУБД (ссылочную целостность, например), так это только оптимизация (ну и еще успокоение нервов админа СУБД), их принципиально можно делать и сервере приложений.
Если данные используемые при проверке больше чем оперативная память, то принципиально нельзя.
GZ>>Если, конечно, тебе не хватает функциональности System.Data.DataSet.
S>Вот люди. Я с ними как с художниками, про новый движок, про специальный язык, а они... Ширее надо мыслить .
Не ширее а ширше. Диревня.
Функциональность датасета (или вернее сказать xsd) достаточно продвинута и достаточна. Еще добавить регуляры, то вообще был бы класс.
С уважением, Gleb.
S>
S> — наличием конструктора с определенной сигнатурой, например, конструктора без параметров, чтобы форма (или другой сервис) могла без проблем создавать новые объекты
S> — полями (без сеттеров), чтобы форма могла присваивать значения в любой последовательности
S> — и функцией validate, которая вызывается формой или другими сервисами автоматически.
S>
S>А для программной работы лучше иметь визнес объекты, написанные по всем правилам ООП:
S>
S> — с такими конструкторами, какие необходимы логически
S> — со свойствами
S> — и т.д.
S>
S>Получается противочечие .
На самом деле это решается паттерном builder. Т.е. есть объект builder, который используется для построения бизнес-объекта. Конструктор пустой, и все свойста для заполнения присутствуют. Как только программист решает, что все данные собраны, он делает newBusinessObject = builder.Create(). И вот Create либо возвращает объект в согласованном состоянии, либо выкидывает исключение. (Кстати, самый простой пример — это StringBuilder (из .NET), правда он как раз всегда может создать строку в согласованном состоянии, но зато мы не увидим строку до того, как всё её содержимое будет собрано).
СА>На самом деле это решается паттерном builder. Т.е. есть объект builder, который используется для построения бизнес-объекта. Конструктор пустой, и все свойста для заполнения присутствуют. Как только программист решает, что все данные собраны, он делает newBusinessObject = builder.Create(). И вот Create либо возвращает объект в согласованном состоянии, либо выкидывает исключение. (Кстати, самый простой пример — это StringBuilder (из .NET), правда он как раз всегда может создать строку в согласованном состоянии, но зато мы не увидим строку до того, как всё её содержимое будет собрано).
Вы это программистам-прикладникам расскажите . Который просто хотят добавить в систему, например, "справочник городов". И хотят сделать это за 2 минуты. И преимущественно кликаньем мышкой по мастерам. И чтобы форма сама построилась. И. т.д. и .т.п.
Они за вами после этого неделю с топором носиться будут.
И ведь поймают в конце-концов. Потому что их больше.
Если серьезно, то мне в этом отношении нравиться Delphi. Вот с какой точки зрения: Delphi не заставляет среднего пользователя писать объектно-ориентированные вещи. Пользователи преимущественно используют уже готовые (т.е. VCL).
А как известно, писать библиотеки в ОО стиле гораздо-гораздо-гораздо сложнее, чем использовать их.
Поэтому в фреймворке я хочу, чтобы всякие справочники (и бизнес объекты для них) создавались мастерами, т.е. визуальными средствами, например, как в 1С.
А использовать их в бизнес логике, естественно программно.