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

Что такое DRY?

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

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

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

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

Принцип DRY в разработке программного обеспечения

DRY (Don't Repeat Yourself — «Не повторяйся») — это фундаментальный принцип разработки программного обеспечения, сформулированный Энди Хантом и Дейвом Томасом в книге «The Pragmatic Programmer». Суть принципа заключается в том, что каждое знание или логика должны иметь единственное, однозначное и авторитетное представление в системе. Это означает, что любая часть кода, логика, конфигурация или данные не должны дублироваться в различных местах приложения.

Почему DRY критически важен?

Повторение кода приводит к множеству проблем:

  • Усложнение поддержки: При необходимости изменить логику или исправить ошибку, разработчик вынужден вносить изменения во всех местах, где эта логика была продублирована. Это увеличивает время работы и вероятность ошибок (например, забыть одно из мест).
  • Рост размера кода: Дублирование делает код избыточным и менее читаемым.
  • Несогласованность: Если изменения внесены не во все копии, система приходит в противоречивое состояние, что порождает трудноуловимые баги.
  • Нарушение принципа единой ответственности (SRP): Одна и та же логика, размазанная по разным классам или модулям, нарушает другие ключевые принципы чистого кода.

DRY на практике в iOS-разработке

В контексте разработки под iOS принцип DRY применяется на всех уровнях.

1. Вынос повторяющейся логики в отдельные методы или функции

Вместо того чтобы копировать один и тот же блок кода для, например, форматирования даты в нескольких UIViewController, мы создаем единственный метод-хелпер.

// ПЛОХО: Дублирование
class ProfileViewController: UIViewController {
    func updateUI() {
        let formatter = DateFormatter()
        formatter.dateStyle = .medium
        formatter.timeStyle = .none
        dateLabel.text = formatter.string(from: user.birthDate)
    }
}

class PostViewController: UIViewController {
    func showPost() {
        let formatter = DateFormatter()
        formatter.dateStyle = .medium
        formatter.timeStyle = .none
        postDateLabel.text = formatter.string(from: post.createdAt)
    }
}

// ХОРОШО: Соблюдение DRY
class DateFormatterHelper {
    static let mediumDateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateStyle = .medium
        formatter.timeStyle = .none
        return formatter
    }()
}

class ProfileViewController: UIViewController {
    func updateUI() {
        dateLabel.text = DateFormatterHelper.mediumDateFormatter.string(from: user.birthDate)
    }
}

class PostViewController: UIViewController {
    func showPost() {
        postDateLabel.text = DateFormatterHelper.mediumDateFormatter.string(from: post.createdAt)
    }
}

2. Создание базовых классов и переиспользование компонентов

Вместо настройки одинакового внешнего вида для всех кнопок в каждом контроллере, можно создать подкласс UIButton с предустановленным стилем.

class PrimaryButton: UIButton {
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupAppearance()
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setupAppearance()
    }

    private func setupAppearance() {
        backgroundColor = .systemBlue
        setTitleColor(.white, for: .normal)
        layer.cornerRadius = 12
        titleLabel?.font = UIFont.boldSystemFont(ofSize: 16)
    }
}
// Теперь `PrimaryButton` можно использовать в любой части проекта, обеспечивая единообразие.

3. Использование протоколов и расширений (Protocols & Extensions)

Протоколы и расширения — мощный инструмент для соблюдения DRY в Swift. Они позволяют добавлять общую функциональность к разным типам.

// Протокол для объектов, которые можно обновить из ViewModel
protocol Configurable {
    associatedtype Model
    func configure(with model: Model)
}

// Базовая реализация для UITableViewCell
extension Configurable where Self: UITableViewCell {
    static var reuseIdentifier: String {
        return String(describing: Self.self)
    }
}

// Конкретная ячейка использует протокол
class UserCell: UITableViewCell, Configurable {
    func configure(with model: User) {
        textLabel?.text = model.name
        detailTextLabel?.text = model.email
    }
}
// В контроллере код становится универсальным и не дублируется для каждого типа ячейки.

4. Централизация констант и конфигураций

Все строковые литералы (например, для UserDefaults ключей, идентификаторов ячеек, уведомлений Notification.Name), цвета, размеры должны быть вынесены в единую структуру или enum.

enum Constants {
    enum UserDefaultsKeys {
        static let isLoggedIn = "isLoggedIn"
        static let username = "username"
    }

    enum CellIdentifiers {
        static let userCell = "UserCell"
        static let productCell = "ProductCell"
    }

    enum AppColors {
        static let primary = UIColor(red: 0.2, green: 0.6, blue: 1.0, alpha: 1.0)
        static let accent = UIColor.systemOrange
    }
}

// Использование
UserDefaults.standard.set(true, forKey: Constants.UserDefaultsKeys.isLoggedIn)
tableView.register(UserCell.self, forCellReuseIdentifier: Constants.CellIdentifiers.userCell)
view.backgroundColor = Constants.AppColors.primary

Важные предостережения

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

Таким образом, следование принципу DRY делает код iOS-приложения более поддерживаемым, тестируемым, гибким и надежным, прямо влияя на скорость разработки и качество конечного продукта.