Тест на вакансию

Пример применения DRY принципа в коде Perl

9 сентября 2025 г.
155

Проблемный код (с нарушением 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, меняем в одном месте
  • Уменьшение количества кода - исключено дублирование
  • Улучшенная читаемость - код становится более декларативным
  • Легче тестировать - валидаторы можно тестировать отдельно
  • Проще поддерживать - меньше шансов сделать ошибку при изменении
Поделиться: