DRY (Don't Repeat Yourself)
DRY — принцип разработки, требующий, чтобы каждый фрагмент знания имел единственное, недвусмысленное, авторитетное представление в системе.
Зачем нужно
Дублирование кода приводит к багам: изменяя логику в одном месте, легко забыть обновить копию. DRY сокращает объём кода, упрощает поддержку и снижает вероятность ошибок.
Где используется
- Рефакторинг повторяющегося кода
- Вынесение общей логики в функции/модули
- CSS-переменные вместо дублирования значений
- HTML-шаблоны и компоненты
- Конфигурации и константы
Предпосылки
DRY в JavaScript
До — код с дублированием
// ПЛОХО: логика валидации повторяется
function createUser(name, email) {
if (!name || name.length < 2) {
throw new Error('Имя должно быть не менее 2 символов');
}
if (!email || !email.includes('@')) {
throw new Error('Некорректный email');
}
return { name, email, createdAt: new Date };
}
function updateUser(user, name, email) {
if (!name || name.length < 2) {
throw new Error('Имя должно быть не менее 2 символов');
}
if (!email || !email.includes('@')) {
throw new Error('Некорректный email');
}
user.name = name;
user.email = email;
user.updatedAt = new Date();
return user;
}
После — DRY
// ХОРОШО: валидация вынесена в отдельные функции
function validateName(name) {
if (!name || name.length < 2) {
throw new Error('Имя должно быть не менее 2 символов');
}
}
function validateEmail(email) {
if (!email || !email.includes('@')) {
throw new Error('Некорректный email');
}
}
function validateUser(name, email) {
validateName(name);
validateEmail(email);
}
function createUser(name, email) {
validateUser(name, email);
return { name, email, createdAt: new Date };
}
function updateUser(user, name, email) {
validateUser(name, email);
Object.assign(user, { name, email, updatedAt: new Date });
return user;
}
DRY в HTML
До
<!-- ПЛОХО: повторяющаяся структура карточки -->
<div class="card">
<img src="photo1.jpg" style="width:200px; border-radius:8px;">
<h3 style="color:#333; font-size:18px;">Продукт 1</h3>
<p style="color:#666; font-size:14px;">Описание продукта 1</p>
<button style="background:#007bff; color:white; padding:8px 16px;">Купить</button>
</div>
<div class="card">
<img src="photo2.jpg" style="width:200px; border-radius:8px;">
<h3 style="color:#333; font-size:18px;">Продукт 2</h3>
<p style="color:#666; font-size:14px;">Описание продукта 2</p>
<button style="background:#007bff; color:white; padding:8px 16px;">Купить</button>
</div>
После — шаблонизация через JS
// ХОРОШО: одна функция генерирует карточки
function renderCard({ image, title, description }) {
return `
<div class="card">
<img src="${image}" class="card__image">
<h3 class="card__title">${title}</h3>
<p class="card__description">${description}</p>
<button class="card__button">Купить</button>
</div>
`;
}
const products = [
{ image: 'photo1.jpg', title: 'Продукт 1', description: 'Описание 1' },
{ image: 'photo2.jpg', title: 'Продукт 2', description: 'Описание 2' },
];
document.querySelector('#catalog').innerHTML =
products.map(renderCard).join('');
DRY в CSS
/* ПЛОХО: повторяющиеся значения */
.header { background-color: #007bff; color: white; }
.button { background-color: #007bff; color: white; }
.link { color: #007bff; }
.footer { border-top: 2px solid #007bff; }
/* ХОРОШО: CSS-переменные */
:root {
--color-primary: #007bff;
--color-text-light: white;
}
.header { background-color: var(--color-primary); color: var(--color-text-light); }
.button { background-color: var(--color-primary); color: var(--color-text-light); }
.link { color: var(--color-primary); }
.footer { border-top: 2px solid var(--color-primary); }
Паттерны повторного использования кода
// 1. Функции — самый простой способ
function formatPrice(amount, currency = 'RUB') {
return new Intl.NumberFormat('ru-RU', {
style: 'currency', currency
}).format(amount);
}
// 2. Модули — экспорт/импорт общей логики
// utils/validation.js
export function isEmail(str) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(str);
}
// 3. Классы — объединение связанных методов
class Validator {
static isEmail(str) { /* ... */ }
static isPhone(str) { /* ... */ }
static isRequired(str) { return str != null && str !== ''; }
}
// 4. Замыкания — фабрики функций
function createValidator(rules) {
return function validate(value) {
return rules.every(rule => rule(value));
};
}
const validatePassword = createValidator([
val => val.length >= 8,
val => /[A-Z]/.test(val),
val => /[0-9]/.test(val),
]);
Когда НЕ применять DRY
DRY — не абсолютный закон. Слепое следование приводит к Wrong Abstraction (неправильной абстракции).
// Две функции ПОХОЖИ, но обслуживают разную логику
// ПЛОХО: насильно объединять их ради DRY
function handlePayment(type, amount, ...args) {
if (type === 'credit') {
// 20 строк логики кредитных карт
} else if (type === 'crypto') {
// 20 строк совершенно другой логики
} else if (type === 'bank') {
// ещё 20 строк
}
}
// ЛУЧШЕ: отдельные функции, даже если есть небольшое повторение
function handleCreditPayment(amount, cardDetails) { /* ... */ }
function handleCryptoPayment(amount, walletAddress) { /* ... */ }
function handleBankPayment(amount, bankAccount) { /* ... */ }
Правило: дублирование дешевле неправильной абстракции. Объединяйте код только когда:
- Повторение происходит 3+ раз (Rule of Three)
- Причина повторения одна и та же
- Изменения всегда применяются ко всем копиям одновременно
Частые ошибки
- DRY ради DRY. Объединение несвязанного кода создаёт хрупкие зависимости
- Преждевременная абстракция. Не абстрагируйте до 3 повторений
- Путать DRY кода и DRY знания. DRY — про знание (бизнес-правило), а не про совпадение строк кода
- Игнорировать DRY в конфигах. Magic numbers и захардкоженные строки — тоже дублирование
Практика
- Найдите повторяющийся код в своём проекте и вынесите в функцию
- Замените магические числа константами:
// До if (age >= 18) { /* ... */ } // После const MINIMUM_AGE = 18; if (age >= MINIMUM_AGE) { /* ... */ } - Создайте модуль
utils.jsс 3-5 переиспользуемыми функциями
Связанные темы
Ресурсы
- The Pragmatic Programmer (Hunt, Thomas) — оригинальное определение DRY
- Sandi Metz — The Wrong Abstraction (статья)
- refactoring.guru — каталог рефакторингов