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

Что такое DRY?

1.0 Junior🔥 161 комментариев
#SOLID и паттерны проектирования

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

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

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

DRY — Don't Repeat Yourself

DRY (Не Повторяй Себя) — один из самых важных принципов чистого кода и профессиональной разработки. Принцип состоит в том, что каждый фрагмент логики, функциональности или информации должен существовать в единственном месте в коде. Если один и тот же код дублируется в нескольких местах, это усложняет поддержку и увеличивает вероятность ошибок.

Проблемы при нарушении DRY

Пример нарушения DRY:

public class UserService {
    public void validateEmail(String email) {
        if (!email.contains("@") || !email.contains(".")) {
            throw new IllegalArgumentException("Invalid email");
        }
    }
    
    public void validateRegistration(String email, String password) {
        // Повторение логики валидации
        if (!email.contains("@") || !email.contains(".")) {
            throw new IllegalArgumentException("Invalid email");
        }
        if (password.length() < 8) {
            throw new IllegalArgumentException("Password too short");
        }
    }
}

Проблемы:

  • Если нужно изменить логику валидации email, нужно менять в двух местах
  • Легко забыть обновить одно место и получить рассинхронизацию
  • Код становится сложнее для понимания и поддержки
  • Выше вероятность ошибок

Решение: применение DRY

public class UserService {
    private EmailValidator validator = new EmailValidator();
    
    public void validateEmail(String email) {
        if (!validator.isValid(email)) {
            throw new IllegalArgumentException("Invalid email");
        }
    }
    
    public void validateRegistration(String email, String password) {
        validateEmail(email);
        if (password.length() < 8) {
            throw new IllegalArgumentException("Password too short");
        }
    }
}

public class EmailValidator {
    public boolean isValid(String email) {
        return email.contains("@") && email.contains(".");
    }
}

Применение DRY: выделение метода

public class PaymentProcessor {
    public void processPayment(double amount) {
        if (amount < 0) {
            throw new IllegalArgumentException("Invalid amount");
        }
        // Логика обработки платежа
    }
    
    public void processRefund(double amount) {
        if (amount < 0) {
            throw new IllegalArgumentException("Invalid amount");
        }
        // Логика обработки возврата
    }
}

// Применение DRY:
public class PaymentProcessor {
    public void processPayment(double amount) {
        validateAmount(amount);
        // Логика обработки платежа
    }
    
    public void processRefund(double amount) {
        validateAmount(amount);
        // Логика обработки возврата
    }
    
    private void validateAmount(double amount) {
        if (amount < 0) {
            throw new IllegalArgumentException("Invalid amount");
        }
    }
}

Применение DRY: выделение класса

// Плохо: SQL запросы дублируются
public class UserRepository {
    public User findById(int id) {
        String sql = "SELECT id, name, email FROM users WHERE id = ?";
        // Выполнение запроса
    }
    
    public List<User> findAll() {
        String sql = "SELECT id, name, email FROM users";
        // Выполнение запроса
    }
}

// Хорошо: SELECT список вынесен в переменную
public class UserRepository {
    private static final String SELECT_COLUMNS = "id, name, email";
    
    public User findById(int id) {
        String sql = "SELECT " + SELECT_COLUMNS + " FROM users WHERE id = ?";
        // Выполнение запроса
    }
    
    public List<User> findAll() {
        String sql = "SELECT " + SELECT_COLUMNS + " FROM users";
        // Выполнение запроса
    }
}

Применение DRY: константы

// Плохо: магические числа
public class OrderService {
    public void applyDiscount(Order order) {
        if (order.getTotal() > 100) {
            order.setTotal(order.getTotal() * 0.9);
        }
    }
    
    public void applyTax(Order order) {
        order.setTotal(order.getTotal() * 1.08);
    }
}

// Хорошо: значения в константах
public class OrderService {
    private static final double DISCOUNT_THRESHOLD = 100;
    private static final double DISCOUNT_RATE = 0.9;
    private static final double TAX_RATE = 1.08;
    
    public void applyDiscount(Order order) {
        if (order.getTotal() > DISCOUNT_THRESHOLD) {
            order.setTotal(order.getTotal() * DISCOUNT_RATE);
        }
    }
    
    public void applyTax(Order order) {
        order.setTotal(order.getTotal() * TAX_RATE);
    }
}

Применение DRY: в тестах

// Плохо: дублирование setup кода
@Test
public void testUser1() {
    User user = new User("John", "john@example.com");
    userService.register(user);
    assertTrue(userService.exists(user.getId()));
}

@Test
public void testUser2() {
    User user = new User("Jane", "jane@example.com");
    userService.register(user);
    assertEquals("jane@example.com", userService.getEmail(user.getId()));
}

// Хорошо: использование @Before
public class UserServiceTest {
    private UserService userService;
    private User testUser;
    
    @Before
    public void setUp() {
        userService = new UserService();
        testUser = new User("John", "john@example.com");
        userService.register(testUser);
    }
    
    @Test
    public void testUserExists() {
        assertTrue(userService.exists(testUser.getId()));
    }
    
    @Test
    public void testUserEmail() {
        assertEquals("john@example.com", userService.getEmail(testUser.getId()));
    }
}

Когда DRY может быть вредным (YAGNI)

Иногда фанатизм в DRY приводит к оверинжинирингу. Нужно понимать разницу:

// Оверинжиниринг: код, который выглядит похоже, но на самом деле разный
public class DataProcessor {
    public void processUserData(String data) {
        validateData(data);
        // User-specific logic
    }
    
    public void processProductData(String data) {
        validateData(data);
        // Product-specific logic
    }
    
    private void validateData(String data) {
        if (data == null || data.isEmpty()) {
            throw new IllegalArgumentException();
        }
    }
}

Здесь оправдано — одна функция валидации используется для двух разных типов данных.

Баланс между DRY и YAGNI

  • Используй DRY когда логика действительно одинаковая
  • Не абстрагируй слишком рано
  • Допускай дублирование на ранних этапах, рефакторь при третьем появлении

DRY — это не просто удаление дублирования, это создание единого источника истины для каждой концепции в коде. Это значительно упрощает поддержу и развитие проекта на протяжении времени.

Что такое DRY? | PrepBro