Как избежать дублирования кода при проверке ввода - PullRequest
3 голосов
/ 19 мая 2009

Предположим, у вас есть подсистема, которая выполняет какую-то работу. Это может быть что угодно. Очевидно, что в точке (точках) входа в эту подсистему будут введены определенные ограничения. Предположим, эта подсистема в первую очередь вызывается через графический интерфейс. Подсистема должна проверить все входные данные, которые она получает, чтобы убедиться, что она действительна. Мы бы не хотели FireTheMissles (), если был неправильный ввод. Пользовательский интерфейс также заинтересован в проверке, потому что он должен сообщить, что пошло не так. Возможно, пользователь забыл указать цель или нацелил ракеты на самой панели запуска. Конечно, вы можете просто вернуть нулевое значение или выдать исключение, но это НЕ дает пользователю СПЕЦИАЛЬНО сообщения о том, что пошло не так (если, конечно, вы не напишите отдельный класс исключений для каждой ошибки, с чем я согласен, если это лучшая практика).

Конечно, даже с исключениями, у вас есть проблема. Пользователь может захотеть узнать, верен ли ввод, ДО того, как нажмете «Огненные пропуски!» кнопка. Вы можете написать отдельную функцию проверки (конечно, IsValid () на самом деле мало помогает, потому что она не говорит вам, что пошло не так), но тогда вы будете вызывать ее из обработчика нажатия кнопки и снова из FireTheMissles ( ) функция (я действительно не знаю, как это изменилось с неопределенной подсистемы на программу запуска ракет). Конечно, это не конец света, но кажется глупым вызывать одну и ту же функцию проверки дважды подряд без каких-либо изменений, особенно если эта функция проверки требует, скажем, вычисления хеш-файла 1 ГБ.

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

Итак, вкратце, как вы достигаете следующего:

  1. Проверка ввода, которая говорит вам не только о том, что что-то пошло не так, но и о том, что конкретно пошло не так.
  2. Возможность запустить эту проверку входных данных без вызова функции, которая на нее опирается.
  3. Нет двойной проверки.
  4. Нет повторяющегося кода.

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

Ответы [ 5 ]

3 голосов
/ 19 мая 2009

«Подсистеме необходимо проверить все входные данные, которые она получает, чтобы убедиться, что она действительна»

Думайте о входных данных не как о списке аргументов, а как о сообщении, после этого становится легче.

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

Теперь пользовательский интерфейс создает сообщение TargetMissiles, и пользовательский интерфейс может проверить его или передать его непосредственно подсистеме MissileFiring, он проверяет, было ли сообщение проверено, если нет, то проверяет его, и в зависимости от того, происходит ли это / не выполняется. Пользовательский интерфейс возвращает сообщение с уже заполненным списком проверок.

Сообщения с проверкой находятся в отдельной библиотеке. Код не дублируется.

Этот звук в порядке?

2 голосов
/ 19 мая 2009

Вот что такое Model-View-Controller .

Вы создаете модель (запуск, который состоит из координат, типов ракет и количества ракет), и модель имеет метод validate, который возвращает список ошибок / предупреждений. Когда вы обновляете модель (по нажатию клавиши , нажатие кнопки), вы вызываете метод validate и показывает пользователю все предупреждения, ошибки и т. Д. (Eclipse имеет небольшую область под панелью инструментов в диалоговом окне, делает это, вы можете посмотреть на это.)

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

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

1 голос
/ 19 мая 2009

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

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

0 голосов
/ 19 мая 2009

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

Контроллеры GUI должны проверять ввод с метаданными и выдавать предупреждения / ошибки, если данные неверны.

Пример: число имеет диапазон. Диапазон предоставляется метаданными, но проверка диапазона обеспечивается элементом управления.

0 голосов
/ 19 мая 2009

Я бы предложил класс проверки ввода, который принимает тип ввода (перечисление) в своем конструкторе и предоставляет открытый метод IsValid.

Метод IsValid должен возвращать логическое ИСТИНА для действительного значения и ЛОЖЬ для недействительного. Он также должен иметь параметр OUT, который принимает строку и назначает сообщение о состоянии этой строке. Вызывающая сторона может свободно игнорировать это сообщение, если оно хочет, или сообщить о нем в графический интерфейс, если это соответствует контексту.

Итак, в псевдокоде (простите за Delphi-подобный синтаксис, но он должен быть доступен каждому):

//different types of data we might want to validate
TValidationType = (vtMissileLaunchCodes, vtFirstName, 
  vtLastName, vtSSN);  

TInputValidator = class
public
  //call the constructor with the validation type
  constructor Create(ValidationType: TValidationType);

  //this should probably be ABSTRACT, implemented by descendants
  //if you took that approach, then you'd have 1 descendant class
  //for each validation type, instead of an enumeration
  function IsValid(InputData: string; var msg: string): boolean;

А затем, чтобы использовать его, вы должны сделать что-то вроде этого:

procedure ValidateForm;
var
  validator: TInputValidator;
begin
  validator := TInputValidator.Create(vtSSN);
  if validator.IsValid(edtSSN.Text,labelErrorMsg.Text) then 
    SaveData;  //it's valid, so save it!
  //if it wasn't valid, then the error msg is in the GUI in "labelErrorMsg".
end;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...