9 сентября 2025 г.
Проблемный код (с нарушением DRY)
Допустим, у нас есть скрипт, который проверяет валидность email и номера телефона в двух разных местах с почти идентичной логикой обработки ошибок.
#!/usr/bin/perl
use strict;
use warnings;
# Где-то в одном месте кода
sub process_user_data {
my ($email, $phone) = @_;
# Проверка email (дублирование логики)
unless ($email =~ /\A[^@]+@[^@]+\.[^@]+\z/) {
print "Error: Invalid email format - $email\n";
log_error("Email validation failed for: $email");
return 0;
}
# Проверка телефона (дублирование логики)
unless ($phone =~ /^\+?[\d\s\-\(\)]{10,}$/) {
print "Error: Invalid phone format - $phone\n";
log_error("Phone validation failed for: $phone");
return 0;
}
# Основная логика обработки
print "User data processed successfully\n";
return 1;
}
# Где-то в другом месте кода
sub update_contact_info {
my ($email, $phone) = @_;
# Тот же код проверки email (дублирование!)
unless ($email =~ /\A[^@]+@[^@]+\.[^@]+\z/) {
print "Error: Invalid email format - $email\n";
log_error("Email validation failed in update: $email");
return 0;
}
# Тот же код проверки телефона (дублирование!)
unless ($phone =~ /^\+?[\d\s\-\(\)]{10,}$/) {
print "Error: Invalid phone format - $phone\n";
log_error("Phone validation failed in update: $phone");
return 0;
}
# Основная логика обновления
print "Contact info updated successfully\n";
return 1;
}
sub log_error {
my $message = shift;
# Запись в лог (упрощенно)
open my $log, '>>', 'app.log' or die "Cannot open log: $!";
print $log "$message\n";
close $log;
}
# Тестирование
process_user_data('test@example.com', '+1234567890');
update_contact_info('invalid-email', 'short');
Рефакторинг с соблюдением DRY
Выносим повторяющуюся логику в отдельные функции:
#!/usr/bin/perl
use strict;
use warnings;
# Вынесенные валидаторы
sub is_valid_email {
my $email = shift;
return $email =~ /\A[^@]+@[^@]+\.[^@]+\z/;
}
sub is_valid_phone {
my $phone = shift;
return $phone =~ /^\+?[\d\s\-\(\)]{10,}$/;
}
# Общая функция обработки ошибок
sub handle_validation_error {
my ($type, $value, $context) = @_;
my $message = "Error: Invalid $type format - $value";
print "$message\n";
log_error("$context validation failed for: $value");
return 0;
}
# Упрощенные основные функции
sub process_user_data {
my ($email, $phone) = @_;
# Используем вынесенные функции
return handle_validation_error('email', $email, 'Email')
unless is_valid_email($email);
return handle_validation_error('phone', $phone, 'Phone')
unless is_valid_phone($phone);
# Основная логика
print "User data processed successfully\n";
return 1;
}
sub update_contact_info {
my ($email, $phone) = @_;
# Используем те же вынесенные функции
return handle_validation_error('email', $email, 'Email in update')
unless is_valid_email($email);
return handle_validation_error('phone', $phone, 'Phone in update')
unless is_valid_phone($phone);
# Основная логика
print "Contact info updated successfully\n";
return 1;
}
sub log_error {
my $message = shift;
open my $log, '>>', 'app.log' or die "Cannot open log: $!";
print $log "$message\n";
close $log;
}
# Тестирование
process_user_data('test@example.com', '+1234567890');
update_contact_info('invalid-email', 'short');
Более продвинутый DRY
Можно пойти дальше и создать валидационный модуль:
# Validator.pm
package Validator;
use strict;
use warnings;
sub new {
my $class = shift;
return bless {}, $class;
}
sub validate_email {
my ($self, $email) = @_;
return $email =~ /\A[^@]+@[^@]+\.[^@]+\z/ ? 1 : 0;
}
sub validate_phone {
my ($self, $phone) = @_;
return $phone =~ /^\+?[\d\s\-\(\)]{10,}$/ ? 1 : 0;
}
sub validate_with_error_handling {
my ($self, $type, $value, $context) = @_;
my $is_valid = $self->can("validate_$type")
? $self->{"validate_$type"}($value)
: die "Unknown validation type: $type";
unless ($is_valid) {
my $message = "Error: Invalid $type format - $value";
print "$message\n";
# Логирование ошибки
return (0, $message);
}
return (1, "");
}
1;
Использование в коде:
#!/usr/bin/perl
use strict;
use warnings;
use Validator;
my $validator = Validator->new();
sub process_user_data {
my ($email, $phone) = @_;
my ($email_valid, $error) = $validator->validate_with_error_handling(
'email', $email, 'Email validation'
);
return 0 unless $email_valid;
my ($phone_valid, $error) = $validator->validate_with_error_handling(
'phone', $phone, 'Phone validation'
);
return 0 unless $phone_valid;
print "User data processed successfully\n";
return 1;
}
Преимущества DRY подхода:
- Единая точка изменения - если нужно изменить валидацию email, меняем в одном месте
- Уменьшение количества кода - исключено дублирование
- Улучшенная читаемость - код становится более декларативным
- Легче тестировать - валидаторы можно тестировать отдельно
- Проще поддерживать - меньше шансов сделать ошибку при изменении