Комментарии (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 — это не просто удаление дублирования, это создание единого источника истины для каждой концепции в коде. Это значительно упрощает поддержу и развитие проекта на протяжении времени.