Подтвердить что ты не робот

Бизнес-объекты, проверки и исключения

Я читал несколько вопросов и ответов об исключениях и их использовании. Похоже, есть сильное мнение о том, что исключения следует поднимать только для исключений, необработанных случаев. Поэтому меня интересует, как валидация работает с бизнес-объектами.

Допустим, у меня есть бизнес-объект с getters/setters для свойств объекта. Допустим, мне нужно проверить, что значение находится между 10 и 20. Это бизнес-правило, поэтому оно принадлежит моему бизнес-объекту. Поэтому мне кажется, что код проверки входит в мой сеттер. Теперь у меня есть привязка к пользовательскому интерфейсу для свойств объекта данных. Пользователь вводит 5, поэтому правило должно выйти из строя, и пользователю не разрешается покидать текстовое поле., Пользовательский интерфейс представляет собой привязку данных к свойству, поэтому сеттер будет вызван, проверка правильности и неудача. Если бы я поднял исключение из своего бизнес-объекта, чтобы сказать, что правило не получилось, пользовательский интерфейс подберет это. Но это, похоже, противоречит предпочитаемому использованию исключений. Учитывая, что его сеттер, у вас действительно будет "результат для сеттера". Если я установил еще один флаг объекта, это означало бы, что пользовательский интерфейс должен проверять этот флаг после каждого взаимодействия с пользовательским интерфейсом.

Итак, как должна работать валидация?

Изменить: я, вероятно, использовал здесь упрощенный пример. Что-то вроде проверки диапазона выше может быть легко обработано пользовательским интерфейсом, но что, если valdation было более сложным, например. бизнес-объект вычисляет число, основанное на вводе, и если это вычисленное число выходит за допустимые пределы, оно должно быть возвращено. Это более сложная логика, которая не должна быть в пользовательском интерфейсе.

Также учитываются дополнительные данные, введенные на основе уже введенного поля. Например, я должен ввести элемент в заказе, чтобы получить определенную информацию, например, под рукой, текущую стоимость и т.д. Пользователь может потребовать, чтобы эта информация принимала решения о дальнейших вводах (количество заказов на количество единиц) или оно может потребоваться в порядке для дальнейшей проверки. Должен ли пользователь вводить другие поля, если элемент недействителен? Какой смысл?

4b9b3361

Ответ 1

Вы хотите немного изучить замечательную работу Paul Stovell относительно проверки данных. Он кратко изложил свои идеи в в этой статье. Мне посчастливилось поделиться своей точкой зрения по этому вопросу, который я реализовал в моих собственных библиотеках.

Вот в словах Павла минусы бросают исключения в сеттерах (на основе образца, где свойство Name не должно быть пустым):

  • Бывают случаи, когда вам действительно нужно иметь пустое имя. Например, в качестве значения по умолчанию для формы "Создать учетную запись".
  • Если вы полагаетесь на это, чтобы проверить данные перед сохранением, вы пропустите случаи, когда данные уже недействительны. Под этим я имею в виду, что если вы загружаете учетную запись из базы данных с пустым именем и не меняете ее, вы, возможно, никогда не узнаете, что она недействительна.
  • Если вы не используете привязку данных, вам нужно написать много кода с блоками try/catch, чтобы показать эти ошибки пользователю. Попытка показать ошибки в форме, когда пользователь заполняет ее, становится очень сложной.
  • Мне не нравится бросать исключения для не исключительных вещей. Пользователь, задающий имя учетной записи для "Supercalafragilisticexpialadocious", не является исключением, это ошибка. Это, конечно, личное дело.
  • Очень сложно получить список всех нарушенных правил. Например, на некоторых веб-сайтах вы увидите сообщения о проверке, такие как "Имя должно быть введено, адрес должен быть введен, адрес электронной почты должен быть введен". Чтобы отобразить это, вам понадобится много блоков try/catch.

И вот основные правила для альтернативного решения:

  • Нет ничего плохого в наличии недопустимого бизнес-объекта, если вы не пытаетесь его сохранить.
  • Любое и все нарушенные правила должны быть извлечены из бизнес-объекта, так что привязка данных, а также ваш собственный код, могут видеть, есть ли ошибки и обрабатывать их соответствующим образом.

Ответ 2

Предполагая, что у вас есть отдельный файл проверки и сохранения (например, сохранение в базу данных), я бы сделал следующее:

  • Пользовательский интерфейс должен выполнять проверку. Не бросайте здесь исключения. Вы можете предупредить пользователя об ошибках и не допустить сохранения записи.

  • Ваш код сохранения базы данных должен вызывать недопустимые исключения аргументов для плохих данных. Здесь имеет смысл сделать это, так как вы не можете продолжить запись базы данных в этот момент. В идеале это никогда не должно происходить, поскольку пользовательский интерфейс должен препятствовать пользователю экономить, но он все равно нуждается в нем для обеспечения согласованности базы данных. Также вы можете вызвать этот код из чего-то другого, кроме пользовательского интерфейса (например, пакетных обновлений), где нет проверки данных пользовательского интерфейса.

Ответ 3

Я всегда был поклонником подхода Rocky Lhotka в структуре CSLA (как упоминал Чарльз). В общем, независимо от того, управляется ли он сеттером или вызывает явный метод Validate, коллекция объектов BrokenRule поддерживается внутри бизнес-объекта. Пользовательский интерфейс просто должен проверить метод IsValid на объекте, который, в свою очередь, проверяет количество BrokenRules и обрабатывает его соответствующим образом. В качестве альтернативы, вы можете легко заставить метод Validate инициировать событие, которое может обрабатывать пользовательский интерфейс (возможно, более чистый подход). Вы также можете использовать список BrokenRules для отображения сообщений об ошибках для использования в сводной форме или рядом с соответствующим полем. Хотя структура CSLA написана в .NET, общий подход может использоваться на любом языке.

Я не думаю, что бросать исключение - лучшая идея в этом случае. Я определенно следую школе мысли, в которой говорится, что Исключения должны быть для исключительных обстоятельств, что простая ошибка проверки не является. По-моему, повышение значения OnValidationFailed было бы более чистым выбором.

Кстати, мне никогда не нравилась идея не дать пользователю покинуть поле, когда оно находится в недопустимом состоянии. Существует так много ситуаций, когда вам может потребоваться временно покинуть поле (возможно, сначала установить другое поле), прежде чем вернуться и исправить недопустимое поле. Я думаю, что это просто лишние неудобства.

Ответ 4

Возможно, вы захотите перенести проверку за пределы получателей и сеттеров. У вас может быть функция или свойство, называемое IsValid, которое будет запускать все правила проверки. t будет заполнять словарь или хеш-таблицу со всеми "Разбитыми правилами". Этот словарь будет доступен для внешнего мира, и вы можете использовать его для заполнения сообщений об ошибках.

Это подход, который используется в CSLA.Net.

Ответ 5

Исключения не должны быть выбраны в качестве обычной части проверки. Валидация, вызванная из бизнес-объектов, является последней строкой защиты, и это должно произойти только в том случае, если пользовательский интерфейс не сможет что-то проверить. Как таковые, они могут рассматриваться как любое другое исключение времени выполнения.

Обратите внимание, что здесь существует разница между определением правил проверки и их применением. Возможно, вы захотите определить (например, код или аннотировать) свои бизнес-правила на уровне бизнес-логики, но вызывать их из пользовательского интерфейса, чтобы они могли обрабатываться способом, соответствующим этому конкретному пользовательскому интерфейсу. Способ обработки будет варьироваться для разных пользовательских интерфейсов, например, для создания веб-приложений на основе форм и веб-приложений ajax. Исключительная проверка подлинности предлагает очень ограниченные возможности для обработки.

Многие приложения дублируют свои правила проверки, например, в javascript, ограничениях объектов домена и ограничениях базы данных. В идеале эта информация будет определяться только один раз, но ее реализация может быть сложной задачей и требует бокового мышления.

Ответ 6

Возможно, вам стоит взглянуть на проверку как на стороне клиента, так и на стороне сервера. Если что-то проскальзывает после проверки на стороне клиента, тогда вы можете свободно генерировать исключение, если ваш бизнес-объект будет признан недействительным.

Один из подходов, который я использовал, - это применить пользовательские атрибуты к свойствам бизнес-объекта, в которых описаны правила проверки. например:.

[MinValue(10), MaxValue(20)]
public int Value { get; set; }

Затем атрибуты могут быть обработаны и использованы для автоматического создания методов проверки на стороне клиента и на стороне сервера, чтобы избежать проблемы дублирования бизнес-логики.

Ответ 7

Я бы определенно защищал как проверку на стороне клиента, так и серверную (или проверку на разных уровнях). Это особенно важно при общении через физические уровни или процессы, поскольку стоимость исключений броска становится все более дорогостоящей. Кроме того, чем дальше по цепочке вы ждете проверки, тем больше времени тратится впустую.

Как использовать Исключения или нет для проверки данных. Я думаю, что нормально использовать исключение в процессе (хотя все еще не предпочтительнее), но вне процесса, вызвать метод проверки бизнес-объекта (например, перед сохранением) и вернуть метод успешности операции вместе с любой валидацией ошибки. Ошибки arent 'исключительны.

Microsoft исключает исключения из бизнес-объектов при отказе проверки. По крайней мере, как работает приложение Application Validation Application Block.

using Microsoft.Practices.EnterpriseLibrary.Validation;
using Microsoft.Practices.EnterpriseLibrary.Validation.Validators;
public class Customer
{
  [StringLengthValidator(0, 20)]
  public string CustomerName;

  public Customer(string customerName)
  {
    this.CustomerName = customerName;
  }
}

Ответ 8

Ваши бизнес-объекты должны генерировать исключения для плохих входов, но эти исключения никогда не должны бросаться в ходе обычного запуска программы. Я знаю, что это звучит противоречиво, поэтому я объясню.

Каждый публичный метод должен проверять свои входы и бросать "ArgumentException", когда они являются неправильными. (И частные методы должны проверять свои входы с помощью "Debug.Assert()", чтобы облегчить разработку, но это другая история.) Это правило о проверке входных данных для общедоступных методов (и свойств, конечно) верно для каждого уровня приложения,

Требования программного интерфейса должны быть указаны в документации по интерфейсу, конечно, и задача вызывающего кода заключается в том, чтобы убедиться, что аргументы верны, и исключения никогда не будут выбрасываться, что означает, что пользовательский интерфейс для проверки входов перед передачей их бизнес-объекту.

Хотя приведенные выше правила почти никогда не будут нарушены, иногда проверка бизнес-объектов может быть очень сложной, и эта сложность не должна навязываться в пользовательском интерфейсе. В этом случае хорошо для интерфейса BO разрешить некоторую свободу действий в том, что он принимает, а затем предоставить явный предикат Validate (out string []), чтобы проверить свойства и дать отзыв о том, что нужно изменить. Но обратите внимание, что в этом случае все еще есть четко определенные требования к интерфейсу, и никаких исключений не нужно бросать (предполагая, что вызывающий код следует правилам).

Следуя этой последней системе, я почти никогда не делаю раннюю проверку на сеттерах свойств, так как это мягко усложняет использование свойств (но в случае, заданном в вопросе, я мог бы). (В стороне, пожалуйста, не мешайте мне выходить из поля только потому, что в нем есть плохие данные. Я получаю clausterphobic, когда я не могу наложить форму, я вернусь и исправлю это через минуту, Я обещаю! Хорошо, мне сейчас стало лучше, извините.)

Ответ 9

Это зависит от того, какую проверку вы будете выполнять и где. Я думаю, что каждый слой приложения можно легко защитить от плохих данных, и его слишком легко сделать, чтобы он не стоил того.

Рассмотрим многоуровневое приложение и требования/условия проверки для каждого уровня. Средний слой, Object, является тем, который, кажется, обсуждается здесь.

  • База данных
    защищает себя от недопустимого состояния с ограничениями столбцов и ссылочной целостностью, что приведет к тому, что код базы данных приложения будет генерировать исключения

  • Object

  • ASP.NET/Windows-формы
    защищает состояние формы (а не объект) с помощью валидаторных подпрограмм и/или элементов управления без использования исключений (winforms не поставляется с валидаторами, но существует прекрасная серия в msdn описывая, как их реализовать)

Скажите, что у вас есть таблица со списком гостиничных номеров, и в каждом ряду есть столбец для количества кроватей, называемых "кроватями". Наиболее разумным типом данных для этого столбца является целое число без знака *. У вас также есть простой объект ole с свойством Int16 *, называемым "Кровати". Проблема в том, что вы можете вставить -4555 в Int16, но когда вы продолжаете хранить данные в базе данных, вы получите Exception. Что хорошо: моей базе данных не следует позволять говорить, что в гостиничном номере меньше нуля, потому что в гостиничном номере не может быть меньше нуля.

* Если ваша база данных может представлять ее, но пусть предположим, что она может * Я знаю, что вы можете просто использовать ushort в С#, но для целей этого примера допустим, что вы не можете

Есть некоторые путаницы в отношении того, должны ли объекты представлять ваш бизнес-объект или должны ли они представлять состояние вашей формы. Конечно, в ASP.NET и Windows Forms форма отлично способна обрабатывать и проверять собственное состояние. Если у вас есть текстовое поле в форме ASP.NET, которое будет использоваться для заполнения того же самого поля Int16, вероятно, вы наложили на свою страницу элемент управления RangeValidator, который проверяет ввод, прежде чем он будет назначен вашему объекту. Это мешает вам вводить значение меньше нуля и, вероятно, мешает вам вводить значение больше, чем, скажем, 30, которое, мы надеемся, будет достаточным для того, чтобы удовлетворить наихудшее общежитие с блохами, которое вы можете себе представить. При обратном вызове вы, вероятно, будете проверять свойство IsValid на странице перед тем, как создавать свой объект, тем самым не позволяя вашему объекту когда-либо представлять меньше нуля кроватей и не позволяя вашему сеттеру когда-либо вызываться со значением, которое оно не должно удерживать.

Но ваш объект по-прежнему способен отображать меньше, чем нулевые кровати, и, опять же, если вы использовали объект в сценарии, не включающем в себя слои, которые имеют встроенную в них проверку (ваша форма и ваша БД), вам удача,

Зачем вам когда-либо быть в этом сценарии? Это должен быть довольно исключительный набор обстоятельств! Поэтому ваш сеттер должен генерировать исключение, когда он получает недопустимые данные. Его никогда не следует бросать, но это может быть так. Вы могли бы написать Windows Form для управления объектом для замены формы ASP.NET и забыть проверить диапазон перед заполнением объекта. Вы можете использовать объект в запланированной задаче, где вообще нет взаимодействия с пользователем, и которая сохраняется в другой, но связанной с ней области базы данных, а не в таблице, к которой привязан объект. В последнем сценарии ваш объект может ввести состояние, в котором оно недействительно, но вы не узнаете, пока результаты других операций не начнут влиять на недопустимое значение. Если вы проверяете их и бросаете исключения, то есть.

Ответ 10

Я склонен полагать, что бизнес-объекты должны генерировать исключения при передаче значений, которые нарушают его бизнес-правила. Тем не менее, похоже, что архитектура привязки данных winforms 2.0 предполагает противоположное, и поэтому большинство людей направляется на поддержку этой архитектуры.

Я согласен с последним ответом на shabbyrobe, что бизнес-объекты должны быть построены для использования и корректно работать в нескольких средах, а не только в среде winforms, например, бизнес-объект может использоваться в веб-службе типа SOA, командной строке интерфейс, asp.net и т.д. Объект должен вести себя корректно и защищать себя от недействительных данных во всех этих случаях.

Аспект, который часто упускается из виду, также происходит в управлении взаимодействием между объектами в отношениях 1-1, 1-n или nn, если они также принимают добавление недействительных коллабораторов и просто поддерживают недопустимый флаг состояния, который должен быть проверять или он должен активно отказываться от добавления недопустимого сотрудничества. Я должен признать, что на меня сильно влияет подход "Оптимизированный объектный моделирование" (SOM) Джилл Никола и др. Но что еще логично.

Следующее - как работать с формами окон. Я рассматриваю создание оболочки UI для бизнес-объектов для этих сценариев.

Ответ 11

Как упоминал Paul Stovell статья, вы можете внедрить безошибочную проверку в своих бизнес-объектах путем реализации интерфейса IDataErrorInfo. Это позволит пользователю оповестить об ошибке пользователя WinForm ErrorProvider и привязка WPF с правила проверки. Логика для проверки свойств ваших объектов хранится в одном методе, а не в каждом из ваших геттеров свойств, и вам не обязательно прибегать к фреймворкам, таким как CSLA или Block Application Application.

Что касается остановки пользователя от изменения фокуса из текстового поля: Прежде всего, это, как правило, не самая лучшая практика. Пользователь может захотеть заполнить форму не по порядку или, если правило проверки зависит от результатов нескольких элементов управления, пользователю может потребоваться заполнить фиктивное значение, чтобы выйти из одного элемента управления, чтобы установить другой элемент управления. Тем не менее, это можно реализовать, установив для свойства Form AllowValidate значение по умолчанию, EnableAllowFocusChange и подписавшись на событие Control.Validating:

    private void textBox1_Validating(object sender, CancelEventArgs e)
    {
        if (textBox1.Text != String.Empty)
        {
            errorProvider1.SetError(sender as Control, "Can not be empty");
            e.Cancel = true;
        }
        else
        {
            errorProvider1.SetError(sender as Control, "");
        }
    }

Использование правил, хранящихся в бизнес-объекте для этой проверки, немного сложнее, так как вызывается событие Validating до изменения фокуса и обновляется связанный с данными бизнес-объект.

Ответ 12

Вам может понравиться подход, принятый каркасом Spring. Если вы используете Java (или .NET), вы можете использовать Spring как есть, но даже если это не так, вы все равно можете использовать этот шаблон; вам просто нужно написать свою собственную реализацию.

Ответ 13

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

Независимо от того, проверяете ли вы свои входные значения, прежде чем отправлять их на бизнес-уровень, зависит от вас, я думаю, что до тех пор, пока вы будете следовать одному и тому же стандарту в своем приложении, вы получите чистый и читаемый код.

Вы можете использовать фреймворк spring, как указано выше, просто будьте осторожны, поскольку большая часть связанного документа указывает на код, который не сильно типизирован, I.E. вы можете получить ошибки во время выполнения, которые вы не могли получить во время компиляции. Это то, чего я стараюсь избегать как можно больше.

Способ, которым мы это делаем в настоящее время, состоит в том, что мы берем с экрана все входные значения, привязываем их к объекту модели данных и генерируем исключение, если значение имеет ошибку.

Ответ 14

По моему опыту, правила проверки редко универсальны во всех экранах/формах/процессах в приложении. Сценарии, подобные этому, являются общими: на странице добавления может быть нормально, чтобы объект Person не имел фамилию, но на странице редактирования он должен иметь фамилию. В этом случае я пришел к выводу, что проверка должна происходить вне объекта, или правила должны быть введены в объект, чтобы правила могли меняться с учетом контекста. Действительным/Недействительным должно быть явное состояние объекта после проверки или одно, которое может быть получено путем проверки коллекции для неудачных правил. Сбой бизнес-правила не является исключением IMHO.

Ответ 15

Рассматривали ли вы сообщение о событии в сеттере, если данные недействительны? Это позволило бы избежать проблемы исключения исключений и устранить необходимость явной проверки объекта на наличие "недопустимого" флага. Вы даже можете передать аргумент, указывающий, какое поле не удалось проверить, чтобы сделать его более многоразовым.

Обработчик события должен быть в состоянии позаботиться о том, чтобы вернуть фокус обратно на соответствующий элемент управления, если это необходимо, и он может содержать любой код, необходимый для уведомления пользователя об ошибке. Кроме того, вы можете просто отказаться от подключения обработчика событий и быть свободным игнорировать отказ проверки при необходимости.

Ответ 16

Я считаю, что это пример, когда бросать исключение - все в порядке. У вашей собственности, вероятно, нет контекста, с помощью которого можно исправить проблему, поскольку такое исключение находится в порядке, и код вызова должен обрабатывать ситуацию, если это возможно.

Ответ 17

Если вход выходит за пределы бизнес-правила, реализованного бизнес-объектом, я бы сказал, что это дело, которое не обрабатывается бизнес-объектом. Поэтому я бы выбрал исключение. Несмотря на то, что сеттер "обрабатывал" 5 в вашем примере, бизнес-объект не будет.

Для более сложных комбинаций ввода требуется метод валидации, иначе вы получите довольно сложные проверки, разбросанные по всему месту.

По-моему, вам нужно будет решить, в какую сторону идти, в зависимости от сложности разрешенного/запрещенного ввода.

Ответ 18

Я думаю, это зависит от того, насколько важна ваша бизнес-модель. Если вы хотите пойти по пути DDD, ваша модель является самой важной вещью. Поэтому вы хотите, чтобы он находился в правильном состоянии во все времена.

На мой взгляд, большинство людей пытаются сделать слишком много (общаться с представлениями, сохраняться в базе данных и т.д.) с объектами домена, но иногда вам нужно больше слоев и лучшее разделение проблем, то есть один или несколько Просмотр моделей. Затем вы можете применять валидацию без исключений в вашей модели просмотра (проверка может отличаться для разных контекстов, например, веб-сервисов/веб-сайта и т.д.) И сохранять исключения в своей бизнес-модели (чтобы модель не была повреждена). Вам понадобится один (или более) уровень Application Service для сопоставления модели View Model с вашей бизнес-моделью. Бизнес-объекты не должны быть загрязнены атрибутами проверки, которые часто связаны с определенными фреймами, например, NHibernate Validator.