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

Что такое DRY?

1.3 Junior🔥 181 комментариев
#Архитектура и паттерны

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

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

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

DRY — Don't Repeat Yourself

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

Основная идея

Если один и тот же код (или логика) появляется в двух или более местах, это признак того, что нужно извлечь общую функциональность в отдельное место и переиспользовать её:

// ❌ ПЛОХО — дублирование кода
function getUserProfile() {
  const firstName = localStorage.getItem('firstName');
  const lastName = localStorage.getItem('lastName');
  const email = localStorage.getItem('email');
  
  return { firstName, lastName, email };
}

function updateUserProfile(firstName, lastName, email) {
  localStorage.setItem('firstName', firstName);
  localStorage.setItem('lastName', lastName);
  localStorage.setItem('email', email);
}

// ✅ ХОРОШО — одна функция для управления
function getStorageItem(key) {
  return localStorage.getItem(key);
}

function setStorageItem(key, value) {
  localStorage.setItem(key, value);
}

function getUserProfile() {
  return {
    firstName: getStorageItem('firstName'),
    lastName: getStorageItem('lastName'),
    email: getStorageItem('email')
  };
}

function updateUserProfile(firstName, lastName, email) {
  setStorageItem('firstName', firstName);
  setStorageItem('lastName', lastName);
  setStorageItem('email', email);
}

Примеры нарушения DRY в Frontend

1. Дублирование в CSS:

/* ❌ ПЛОХО */
.button-primary {
  padding: 12px 24px;
  border-radius: 8px;
  font-size: 16px;
  font-weight: 600;
  border: none;
  cursor: pointer;
}

.button-secondary {
  padding: 12px 24px;
  border-radius: 8px;
  font-size: 16px;
  font-weight: 600;
  border: none;
  cursor: pointer;
}

/* ✅ ХОРОШО — используй переменные и миксины */
:root {
  --button-padding: 12px 24px;
  --button-radius: 8px;
  --button-font: 600 16px/1 sans-serif;
}

.button-base {
  padding: var(--button-padding);
  border-radius: var(--button-radius);
  font: var(--button-font);
  border: none;
  cursor: pointer;
}

.button-primary {
  @extend .button-base;
  background-color: #007bff;
}

.button-secondary {
  @extend .button-base;
  background-color: #6c757d;
}

2. Дублирование логики в React компонентах:

// ❌ ПЛОХО — один и тот же хук в разных компонентах
function UserProfile() {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  
  useEffect(() => {
    setLoading(true);
    fetch('/api/user')
      .then(res => res.json())
      .then(data => setUser(data))
      .catch(err => setError(err))
      .finally(() => setLoading(false));
  }, []);
  
  return <div>{user?.name}</div>;
}

function UserSettings() {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  
  useEffect(() => {
    setLoading(true);
    fetch('/api/user')
      .then(res => res.json())
      .then(data => setUser(data))
      .catch(err => setError(err))
      .finally(() => setLoading(false));
  }, []);
  
  return <input value={user?.name} />;
}

// ✅ ХОРОШО — извлеки в кастомный хук
function useUser() {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  
  useEffect(() => {
    setLoading(true);
    fetch('/api/user')
      .then(res => res.json())
      .then(data => setUser(data))
      .catch(err => setError(err))
      .finally(() => setLoading(false));
  }, []);
  
  return { user, loading, error };
}

function UserProfile() {
  const { user } = useUser();
  return <div>{user?.name}</div>;
}

function UserSettings() {
  const { user } = useUser();
  return <input value={user?.name} />;
}

3. Дублирование констант и конфигов:

// ❌ ПЛОХО
const API_BASE = 'https://api.example.com';
// На 10 местах в коде повторяется
fetch(`${API_BASE}/users`);
fetch(`${API_BASE}/posts`);
fetch(`${API_BASE}/comments`);

// ✅ ХОРОШО — один источник истины
const API_CONFIG = {
  BASE_URL: 'https://api.example.com',
  TIMEOUT: 5000,
  RETRY_COUNT: 3
};

const apiClient = {
  get: (endpoint) => fetch(`${API_CONFIG.BASE_URL}${endpoint}`),
  post: (endpoint, data) => fetch(`${API_CONFIG.BASE_URL}${endpoint}`, { method: 'POST', body: JSON.stringify(data) })
};

apiClient.get('/users');
apiClient.get('/posts');

Когда НЕ применяй DRY

Хотя DRY очень важен, есть исключения:

  1. Случайное совпадение кода — если две функции случайно содержат одинаковый код, но решают разные проблемы, их объединение может привести к излишней связанности:
// Две разные функции, хотя логика похожа
function validateEmail(email) {
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}

function validatePassword(password) {
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(password); // Неправильная логика!
}

// Правильно — разные валидаторы
function validateEmail(email) {
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}

function validatePassword(password) {
  return password.length >= 8 && /[A-Z]/.test(password);
}
  1. Сложное объединение — если объединение кода усложнит его сильнее, чем дублирование, лучше оставить как есть.

Преимущества DRY

  • Упрощение поддержки — изменение логики в одном месте автоматически применяется везде
  • Уменьшение багов — исправление ошибки в одной функции фиксит её везде
  • Улучшение читаемости — меньше кода для понимания
  • Масштабируемость — проще добавлять новый функционал

DRY — это не просто о коде, это о поддерживаемости и качестве проекта.