← Назад к вопросам
Когда не стоит использовать дублирующий код?
2.0 Middle🔥 92 комментариев
#Архитектура и паттерны
Комментарии (2)
🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Дублирующий код: когда его использовать нельзя (DRY принцип)
Суть принципа DRY (Don't Repeat Yourself)
DRY — один из фундаментальных принципов чистого кода. Дублирование кода приводит к техническому долгу, ошибкам при обновлении и сложности поддержки.
Правило: Если код повторяется 3+ раза — пора рефакторить.
Когда НЕЛЬЗЯ использовать дублирующий код
1. Одинаковая бизнес-логика
// ❌ ПЛОХО — дублирование валидации пользователя
async function registerUser(email: string, password: string) {
if (!email || !email.includes('@')) {
throw new Error('Invalid email');
}
if (password.length < 8) {
throw new Error('Password too short');
}
// ... регистрация
}
async function updateUserProfile(email: string, password: string) {
if (!email || !email.includes('@')) {
throw new Error('Invalid email');
}
if (password.length < 8) {
throw new Error('Password too short');
}
// ... обновление
}
// ✅ ХОРОШО — выносим валидацию в функцию
function validateUserInput(email: string, password: string) {
if (!email || !email.includes('@')) {
throw new Error('Invalid email');
}
if (password.length < 8) {
throw new Error('Password too short');
}
}
async function registerUser(email: string, password: string) {
validateUserInput(email, password);
// ... регистрация
}
async function updateUserProfile(email: string, password: string) {
validateUserInput(email, password);
// ... обновление
}
2. Повторяющаяся обработка ошибок
// ❌ ПЛОХО — дублирование обработки ошибок
async function getUser(id: string) {
try {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
logger.error(`HTTP ${response.status}:`, response.statusText);
throw new Error('Failed to fetch user');
}
return await response.json();
} catch (error) {
logger.error('Network error:', error);
throw error;
}
}
async function getPost(id: string) {
try {
const response = await fetch(`/api/posts/${id}`);
if (!response.ok) {
logger.error(`HTTP ${response.status}:`, response.statusText);
throw new Error('Failed to fetch post');
}
return await response.json();
} catch (error) {
logger.error('Network error:', error);
throw error;
}
}
// ✅ ХОРОШО — выносим HTTP логику в обёртку
async function fetchJson<T>(url: string): Promise<T> {
try {
const response = await fetch(url);
if (!response.ok) {
logger.error(`HTTP ${response.status}:`, response.statusText);
throw new Error(`HTTP ${response.status}`);
}
return await response.json();
} catch (error) {
logger.error('Network error:', error);
throw error;
}
}
async function getUser(id: string) {
return fetchJson(`/api/users/${id}`);
}
async function getPost(id: string) {
return fetchJson(`/api/posts/${id}`);
}
3. Дублирование структуры объектов
// ❌ ПЛОХО — повторяющийся DTO маппинг
async function getUserDTO(id: string) {
const user = await db.users.findById(id);
return {
id: user.id,
name: user.name,
email: user.email,
createdAt: user.createdAt,
};
}
async function getUsersListDTO(ids: string[]) {
const users = await db.users.findByIds(ids);
return users.map(user => ({
id: user.id,
name: user.name,
email: user.email,
createdAt: user.createdAt,
}));
}
// ✅ ХОРОШО — выносим трансформацию
function toUserDTO(user: User): UserDTO {
return {
id: user.id,
name: user.name,
email: user.email,
createdAt: user.createdAt,
};
}
async function getUserDTO(id: string) {
const user = await db.users.findById(id);
return toUserDTO(user);
}
async function getUsersListDTO(ids: string[]) {
const users = await db.users.findByIds(ids);
return users.map(toUserDTO);
}
4. Дублирование SQL запросов
// ❌ ПЛОХО — один и тот же WHERE clause повторяется
async function getActiveUsers() {
return db.query(`
SELECT * FROM users
WHERE deleted_at IS NULL
AND status = 'active'
`);
}
async function countActiveUsers() {
return db.query(`
SELECT COUNT(*) FROM users
WHERE deleted_at IS NULL
AND status = 'active'
`);
}
async function deleteActiveUsers() {
return db.query(`
DELETE FROM users
WHERE deleted_at IS NULL
AND status = 'active'
`);
}
// ✅ ХОРОШО — использовать query builder или параметризованные запросы
const ACTIVE_USER_FILTER = {
where: {
deletedAt: null,
status: 'active'
}
};
async function getActiveUsers() {
return db.users.find(ACTIVE_USER_FILTER);
}
async function countActiveUsers() {
return db.users.count(ACTIVE_USER_FILTER);
}
async function deleteActiveUsers() {
return db.users.deleteMany(ACTIVE_USER_FILTER);
}
5. Дублирование констант
// ❌ ПЛОХО — магические числа повторяются
function validatePassword(pwd: string): boolean {
return pwd.length >= 8 && pwd.length <= 128;
}
function generatePasswordResetToken(): string {
return randomString(128); // Откуда 128?
}
async function storePassword(hash: string): Promise<void> {
if (hash.length > 128) {
throw new Error('Hash too long');
}
}
// ✅ ХОРОШО — выносим константы
const PASSWORD_CONFIG = {
MIN_LENGTH: 8,
MAX_LENGTH: 128,
RESET_TOKEN_LENGTH: 128,
} as const;
function validatePassword(pwd: string): boolean {
return pwd.length >= PASSWORD_CONFIG.MIN_LENGTH &&
pwd.length <= PASSWORD_CONFIG.MAX_LENGTH;
}
function generatePasswordResetToken(): string {
return randomString(PASSWORD_CONFIG.RESET_TOKEN_LENGTH);
}
async function storePassword(hash: string): Promise<void> {
if (hash.length > PASSWORD_CONFIG.MAX_LENGTH) {
throw new Error('Hash too long');
}
}
Исключения из DRY (когда дублирование допустимо)
1. Случайное совпадение кода (False DRY)
// Это НЕ одна логика, они независимые!
// ❌ НЕПРАВИЛЬНО объединять:
function calculateUserScore(purchases: number, reviews: number): number {
return purchases * 10 + reviews * 5;
}
function calculateProductRecommendationScore(
salesCount: number,
ratingCount: number
): number {
return salesCount * 10 + ratingCount * 5; // Выглядит как дубль!
}
// ✅ ПРАВИЛЬНО — оставить отдельно:
// Это разные метрики с разным смыслом!
// Если в одной нужно изменить логику — нельзя менять другую
2. Разные уровни абстракции
// ✅ Допустимо дублирование на разных уровнях:
// Service level (бизнес-логика)
class UserService {
async createUser(data: CreateUserDTO) {
if (!data.email.includes('@')) {
throw new Error('Invalid email');
}
return await this.repository.save(data);
}
}
// API level (валидация входа)
@Post('/users')
async createUserEndpoint(@Body() body: any) {
if (!body.email || !body.email.includes('@')) {
return { error: 'Invalid email' };
}
return await this.userService.createUser(body);
}
// Это НЕ дублирование! Разные слои, разные ответственности.
// API проверяет формат данных, Service проверяет бизнес-правила.
3. Производительность и читаемость
// ✅ Иногда дублирование УЛУЧШАЕТ читаемость:
// Отдельные функции явно показывают намерение
export function validateEmail(email: string): boolean {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
export function validatePhoneNumber(phone: string): boolean {
return /^\+?[\d\s-]{10,}$/.test(phone);
}
// Хотя regex похожи, их объединение усложнит код:
// ❌ validateContact(value, 'email') — менее понятно
Как рефакторить дублирование
Шаги:
- Найти повторяющийся паттерн
- Выделить в отдельную функцию/класс
- Параметризировать различия
- Использовать везде
// Было:
function processUserData(user: User) {
const name = user.firstName + ' ' + user.lastName;
const email = user.contactEmail || user.primaryEmail;
console.log(`User: ${name}, ${email}`);
}
function processAdminData(admin: Admin) {
const name = admin.firstName + ' ' + admin.lastName;
const email = admin.contactEmail || admin.primaryEmail;
console.log(`Admin: ${name}, ${email}`);
}
// Стало:
interface Person {
firstName: string;
lastName: string;
contactEmail?: string;
primaryEmail: string;
}
function getDisplayName(person: Person): string {
return `${person.firstName} ${person.lastName}`;
}
function getEmail(person: Person): string {
return person.contactEmail || person.primaryEmail;
}
function displayPerson(person: Person, role: string) {
console.log(`${role}: ${getDisplayName(person)}, ${getEmail(person)}`);
}
Заключение
Правила:
- DRY — главный принцип, соблюдай его
- Три раза — уже много — рефакторь после третьего повтора
- False DRY — не объединяй случайно совпадающий код
- Читаемость выше DRY — если объединение усложнит код, лучше дублировать
- Разные слои — нормально, если одна логика на API и другая на Service
Правильное применение DRY — это баланс между DRY и других принципов (KISS, SOLID).