← Назад к вопросам

Когда работает правило DRY?

1.8 Middle🔥 161 комментариев
#Архитектура и паттерны

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Принцип DRY (Don't Repeat Yourself) в контексте PHP Backend

DRY (Don't Repeat Yourself) — один из фундаментальных принципов разработки программного обеспечения, который гласит: "Каждое знание или логика должны иметь единственное, однозначное и авторитетное представление в системе". В PHP-разработке этот принцип применяется систематически, но требует взвешенного подхода.

Когда правило DRY работает эффективно

1. Повторяющаяся бизнес-логика и вычисления

// ПЛОХО: Логика расчета скидки дублируется
class OrderProcessor {
    public function calculateDiscount($amount) {
        if ($amount > 1000) {
            return $amount * 0.1;
        }
        return 0;
    }
}

class InvoiceGenerator {
    public function applyDiscount($amount) {
        if ($amount > 1000) {
            return $amount * 0.1;
        }
        return 0;
    }
}

// ХОРОШО: Единый источник логики
class DiscountCalculator {
    public static function calculate($amount) {
        if ($amount > 1000) {
            return $amount * 0.1;
        }
        return 0;
    }
}

2. Валидация и проверка данных

// Абстракция правил валидации в одном месте
class UserValidator {
    private const EMAIL_PATTERN = '/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/';
    
    public static function validateEmail(string $email): bool {
        return preg_match(self::EMAIL_PATTERN, $email) === 1;
    }
    
    public static function validatePasswordStrength(string $password): bool {
        return strlen($password) >= 8 
            && preg_match('/[A-Z]/', $password)
            && preg_match('/[0-9]/', $password);
    }
}

3. Конфигурация и настройки приложения

// Централизованное хранение конфигурации
class DatabaseConfig {
    private static $config = [
        'host' => 'localhost',
        'port' => 3306,
        'database' => 'app_db',
        'charset' => 'utf8mb4'
    ];
    
    public static function getDsn(): string {
        return sprintf(
            'mysql:host=%s;port=%d;dbname=%s;charset=%s',
            self::$config['host'],
            self::$config['port'],
            self::$config['database'],
            self::$config['charset']
        );
    }
}

4. Шаблоны запросов к базе данных

// Репозиторий как единое место для работы с сущностью
class UserRepository {
    private $connection;
    
    public function __construct(PDO $connection) {
        $this->connection = $connection;
    }
    
    public function findActiveUsers(): array {
        $stmt = $this->connection->prepare("
            SELECT * FROM users 
            WHERE status = 'active' 
            AND deleted_at IS NULL
        ");
        $stmt->execute();
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
    
    public function findUsersByRole(string $role): array {
        $stmt = $this->connection->prepare("
            SELECT * FROM users 
            WHERE role = :role 
            AND deleted_at IS NULL
        ");
        $stmt->execute(['role' => $role]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
}

Когда НЕ следует слепо применять DRY

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

2. Разные причины для изменения Если два фрагмента кода могут изменяться по разным причинам (принцип единственной ответственности), их объединение нарушит SRP.

3. Преждевременная абстракция Создание абстракций до того, как станут понятны реальные шаблоны использования, часто приводит к излишней сложности.

4. Простые, изолированные дублирования Небольшие, локальные повторы, которые маловероятно будут изменяться, иногда лучше оставить как есть для сохранения ясности.

Практические рекомендации для PHP-разработчика

  1. Используйте трейты для горизонтального повторного использования
trait Timestampable {
    protected $createdAt;
    protected $updatedAt;
    
    public function setTimestamps() {
        $this->createdAt = new DateTime();
        $this->updatedAt = new DateTime();
    }
}
  1. Применяйте сервисные классы для сложной логики
  2. Создавайте базовые классы-родители для общих методов
  3. Используйте шаблоны проектирования (Strategy, Template Method)
  4. Внедряйте Dependency Injection для переиспользуемых компонентов

Баланс между DRY и другими принципами

Важно помнить, что DRY — не абсолютная догма, а руководство к действию. Иногда его приходится нарушать в пользу:

  • KISS (Keep It Simple, Stupid) — простота важнее устранения дублирования
  • YAGNI (You Aren't Gonna Need It) — не добавляйте функциональность "на будущее"
  • Читаемости кода — понятный код с небольшим дублированием лучше сложной абстракции

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