Исходный код
var req_date = document.getElementById('requested_date').value.split('-');
if (req_date.length!=3 || new Number(req_date[0])<1990 || new Number(req_date[0])>2100
|| new Number(req_date[1])<1 || new Number(req_date[1])>12 || new Number(req_date[2])<1) {
alert('Requested date is not valid, please re-enter.');
return false;
}
var ef_date = document.getElementById('effective_date').value.split('-');
if (ef_date.length!=3 || new Number(ef_date[0])<1990 || new Number(ef_date[0])>2100
|| new Number(ef_date[1])<1 || new Number(ef_date[1])>12 || new Number(ef_date[2])<1) {
alert('Effective date is not valid, please re-enter.');
return false;
}
var ex_date = document.getElementById('expiration_date').value.split('-');
if (ex_date.length!=3 || new Number(ex_date[0])<1990 || new Number(ex_date[0])>2100
|| new Number(ex_date[1])<1 || new Number(ex_date[1])>12 || new Number(ex_date[2])<1) {
alert('Expiration date is not valid, please re-enter.');
return false;
}
var month_days = new Array(31,29,31,30,31,30,31,31,30,31,30,31);
if (new Number(req_date[0])%4) {
month_days[1] = 28;
}
if ((new Number(req_date[2])>month_days[new Number(req_date[1])-1]) ) {
alert('Requested date is not valid, please re-enter.');
return false;
}
if (new Number(ef_date[0])%4) {
month_days[1] = 28;
}
if ((new Number(ef_date[2])>month_days[new Number(ef_date[1])-1]) ) {
alert('Effective date is not valid, please re-enter.');
return false;
}
if (new Number(ex_date[0])%4) {
month_days[1] = 28;
}
if ((new Number(ex_date[2])>month_days[new Number(ex_date[1])-1]) ) {
alert('Expiration date is not valid, please re-enter.');
return false;
}
Как можно отрефакторить и улучшить исходный код
Разделение ответственностей
Здесь один фрагмент кода выполняет сразу много обязанностей (судя по операторам return
это код функции):
- получает введённое пользователем значение даты
- применяет валидацию к введённым датам
- проверяет валидность даты
- выводит сообщения об ошибках
- сопоставляет машинное и человеческое названия полей
Под большинство обязанностей нужно отвести отдельные функции. Желательно организовать обработку и вывод ошибок таким образом, чтобы пользователя не заваливало алертами, если неправильные данные окажутся в нескольких полях.
function validate(errors, field_name, value, method) {
const field_errors = method(value);
if (field_errors) {
errors.push({
name: field_name,
errors: field_errors,
});
}
}
function validateDate(date) {
// ...
}
function reportValidationErrors(errors) {
let messages = [];
for (const field of errors) {
for (const field_error of field.errors) {
messages.push(`${field.name}: ${field_error}`);
}
}
alert(messages.join("\n"));
}
let validation_errors = [];
validate(validation_errors, 'requested_date', document.getElementById('requested_date').value, validateDate);
validate(validation_errors, 'effective_date', document.getElementById('effective_date').value, validateDate);
validate(validation_errors, 'expiration_date', document.getElementById('expiration_date').value, validateDate);
reportValidationErrors(validation_errors);
Теперь верхнеуровневый код знает только о том, как получить значения полей и как в целом организовать процесс валидации, остальные ответственности разбежались по отдельным функциям:
- как валидировать поле формы (без привязки к типу)
- как валидировать дату (без привязки источника: поле или не поле)
- как показать пользователю ошибки валидации
Функцию для проверки валидности даты я намеренно оставил пустой, так как исходный алгоритм для этого дела нуждается в доработке.
Для сообщений об ошибках валидации каждого поля я отвёл целый массив, так как в общем случае значение в поле может нарушать сразу несколько правил валидации.
Рефакторинг алгоритма валидации даты
Проверка валидности состоит из двух шагов:
- проверить, что введённая дата в принципе валидна
- проверить, что дата находится в требуемом диапазоне (от 1990 до 2100 года)
В исходном коде эти шаги перемешаны между собой.
Очевидно, что для первого шага нет смысла писать какой-либо код, когда существует функция Date.parse
.
function validateDate(date_string) {
const date = Date.parse(date_string);
if (isNaN(date)) {
return ['Неправильная дата'];
}
const year = date.getFullYear();
if (year < 1990 || year > 2100) {
return ['Год должен находиться в диапазоне от 1990 до 2100'];
}
return null;
}
Теория
- Принцип единственного знания
- Принцип единственной реализации
- Рефакторинг «извлечение метода»