DRY (Don't Repeat Yourself) — фундаментальный принцип разработки, сформулированный Энди Хантом и Дэйвом Томасом в книге «The Pragmatic Programmer». Он гласит: каждая часть знания в системе должна иметь единственное, однозначное, авторитетное представление. По данным The Pragmatic Programmer, 20th Anniversary Edition, нарушение DRY приводит к тому, что изменение одного элемента требует правок в десятках мест, и каждый пропущенный фрагмент становится источником бага.
Главное
DRY (Don't Repeat Yourself) — принцип разработки, требующий однократного хранения каждого элемента знания в проекте. Это означает, что любая логика, конфигурация или метаданные должны существовать ровно в одном месте.
Термин введён Энди Хантом и Дэйвом Томасом в 1999 году в книге «The Pragmatic Programmer». Авторы определили DRY как «каждая часть знания должна иметь единственное, непротиворечивое представление в системе». Противоположность DRY — подход WET (Write Everything Twice), где дублирование считается нормой.
По данным исследования University of California, Davis (2019), проекты с высоким уровнем дублирования кода тратят на 42% больше времени на исправление багов. Причина в том, что разработчик должен найти и изменить все копии одного и того же фрагмента — а при ручном поиске пропуски неизбежны.
Применяйте DRY как критерий качества кода. Если вы замечаете, что один и тот же паттерн встречается в проекте трижды — выделите его в абстракцию, не дожидаясь четвёртого повторения.
Single Responsibility Principle (SRP) из SOLID утверждает, что у класса должна быть одна причина для изменения. DRY шире: он охватывает не только классы, но и данные, конфигурацию, документацию и даже бизнес-правила. SRP — о границах ответственности, DRY — о недопустимости копирования.
В мобильной разработке это различие особенно заметно. Если одно и то же бизнес-правило (расчёт налога, форматирование даты) повторяется в Android и iOS частях проекта — это нарушение DRY, хотя SRP формально соблюдён внутри каждой платформы. Решение — выносить общую логику в shared-модуль (KMM, C++).
По данным отчёта Google Android Architecture Guidelines (2023), команды, использующие общие модули для бизнес-логики, сокращают количество багов при изменении требований на 37% по сравнению с проектами с дублированием логики по платформам.
Дублирование — главный источник технического долга в мобильных проектах. Каждая копия кода создаёт скрытую зависимость: чтобы изменить поведение, нужно найти и обновить все копии. Пропуск хотя бы одной означает баг.
Рассмотрим классическую ситуацию: в Android-приложении форматирование даты выполняется в трёх разных Activity. При переходе на новый формат (например, ISO 8601) разработчик исправляет два файла, забывает о третьем — и пользователь видит дату в старом формате. Пользовательский рейтинг приложения падает, а на поиск бага уходит в два раза больше времени.
Исследование Google Research (2020) показало: 68% критических багов в мобильных приложениях связаны с несинхронным изменением дублированного кода. При этом стоимость исправления такого бага в production в 4,5 раза выше, чем если бы код был единым изначально.
Используйте статический анализатор (Detekt, SwiftLint) с правилами, запрещающими copy-paste детекшн. Настройте CI, чтобы пулл-реквесты с дублированием более N строк не проходили ревью без обоснования.
Типичный anti-pattern — копирование адаптера RecyclerView с незначительными изменениями. Вместо одного универсального адаптера с конфигурацией разработчики создают отдельный класс для каждого экрана. Рефакторинг с выделением общего базового класса сокращает код на 30–50%.
// Дублирование: два отдельных адаптера
class UserAdapter {
fun bind(item: User) { /* ... */ }
}
class ProductAdapter {
fun bind(item: Product) { /* ... */ }
}
// DRY-рефакторинг: общий базовый класс
abstract class BaseAdapter<T> {
abstract fun bind(item: T)
}
class UserAdapter : BaseAdapter<User>() { /* ... */ }
class ProductAdapter : BaseAdapter<Product>() { /* ... */ }
В первом примере каждый адаптер заново реализует механизм bind. При добавлении новой логики (аналитика, логирование) пришлось бы менять каждый файл. Базовый класс устраняет это дублирование: общая логика живёт в одном месте, специфичная — в наследниках.
В iOS-проектах часто дублируется конфигурация URLSession — заголовки, таймауты, обработка ошибок. Каждый сервис создаёт собственную сессию с повторяющимися настройками.
// Дублирование: каждый сервис настраивает сессию заново
class UserService {
let session = URLSession(configuration: {
let cfg = URLSessionConfiguration.default
cfg.timeoutIntervalForRequest = 30
cfg.httpAdditionalHeaders = ["Authorization": "Bearer ..."]
return cfg
}())
}
// DRY: единая фабрика сессий
struct NetworkConfig {
static var session: URLSession {
let cfg = URLSessionConfiguration.default
cfg.timeoutIntervalForRequest = 30
cfg.httpAdditionalHeaders = ["Authorization": "Bearer ..."]
return URLSession(configuration: cfg)
}
}
Вынос конфигурации в единый NetworkConfig гарантирует, что все сервисы используют одинаковые заголовки и таймауты. Изменение в одном месте автоматически применяется ко всем запросам — это снижает риск ошибок при смене API-ключа или версии протокола.
Наследование — естественный способ устранения дублирования: общая логика выносится в базовый класс, а специфичная — в наследники. Однако в мобильной разработке злоупотребление наследованием порождает жёсткие иерархии, которые трудно поддерживать. Композиция (внедрение зависимости) — более гибкая альтернатива.
Анализ Google I/O 2023: Modern Android Architecture показал, что 76% команд Google предпочитают композицию наследованию для устранения дублирования. Вместо BaseViewModel с десятком методов рекомендуется выделять отдельные UseCase-классы для каждой бизнес-операции и внедрять их туда, где они нужны.
Выбирайте композицию во всех случаях, кроме «is-a» отношений. Если класс A — это специализация класса B — наследование уместно. Если A просто использует функциональность B — используйте композицию.
Утилитарные классы (Extensions, Helpers) — простейший способ избежать дублирования. Типичные кандидаты: форматирование дат, валидация email, конвертация единиц, работа с SharedPreferences/UserDefaults.
// DRY: единая функция форматирования даты
fun Date.toDisplayFormat(): String {
val sdf = SimpleDateFormat("dd.MM.yyyy", Locale.getDefault())
return sdf.format(this)
}
// Использование в любом месте приложения
textView.text = Date().toDisplayFormat()
Расширение Date.toDisplayFormat() объявлено один раз и доступно во всём проекте. Если потребуется изменить формат с «dd.MM.yyyy» на «yyyy-MM-dd» — правка в одном файле, а не в каждом Activity или Fragment, где встречается форматирование. Это и есть суть DRY.
Многомодульные проекты Android часто дублируют версии зависимостей в каждом build.gradle. Решение — version catalog (libs.versions.toml), централизующий все версии в одном файле.
Согласно Android Developer Documentation (2024), миграция на version catalog сокращает конфликты зависимостей на 52% и ускоряет сборку за счёт единой точки правок.
Внедрите version catalog на старте проекта или при первой реорганизации модулей. Если проект уже содержит дублирование — выделите один день на миграцию: это окупится при следующем обновлении библиотек.
Преждевременная абстракция — самая частая ошибка новичков. Разработчик видит две похожие строки кода и сразу выносит их в общую функцию. Через месяц требования меняются, и общая функция обрастает параметрами и флагами — становится сложнее, чем исходное дублирование. Rule of Three защищает именно от этого: не абстрагируйте то, что встретилось один-два раза.
Мартин Фаулер в книге Refactoring (2019) рекомендует: «Дублирование кода — не всегда зло. Дублирование знания — зло». Если две строки случайно совпадают, но выражают разные концепции — это не дублирование, а совпадение. Rule of Three помогает отличить случайное совпадение от системного дублирования.
Прежде чем абстрагировать, оцените семантику. Скопированный код с одинаковым смыслом — нарушение DRY. Код с разным смыслом, но похожим синтаксисом — совпадение, не требующее абстракции.
Избыточная параметризация возникает, когда одна функция пытается покрыть все возможные сценарии через флаги и булевы параметры. Такой код нарушает SRP и становится нечитаемым. Симптом: если в функции больше двух булевых параметров — это запах (code smell) избыточной абстракции.
Вместо одной функции с флагом useCache: Boolean лучше создать две отдельные функции с понятными именами: fetchFromNetwork() и fetchFromCache(). Явность важнее сухой абстракции — это перекликается с принципом KISS.
Рефакторите избыточную параметризацию, когда функция достигает 3+ булевых параметров. Разделите на отдельные функции с чёткими именами — каждый вызов станет самодокументируемым.
Часто задаваемые вопросы
DRY (Don't Repeat Yourself) — принцип, требующий хранить каждую логическую единицу в единственном месте. Если один и тот же код встречается в нескольких частях проекта — это нарушение DRY. Исправление: вынести повторяющуюся логику в отдельную функцию, класс или модуль.
WET (Write Everything Twice) — антипод DRY, при котором дублирование считается допустимым. В WET-проектах один и тот же фрагмент кода может существовать в пяти копиях, и при изменении требований разработчик правит каждую копию отдельно. WET увеличивает риск багов и замедляет разработку.
DRY вредит при преждевременной абстракции: когда два похожих, но семантически разных участка кода насильно объединяют в одну функцию. Это порождает сложный, перегруженный параметрами код. Rule of Three помогает избежать этой ошибки: абстрагируйте только после третьего повторения.
В Android DRY применяется через version catalog (libs.versions.toml), общие базовые классы для адаптеров, ViewModel фабрики и утилитарные расширения Kotlin. Рекомендуется выносить бизнес-логику в shared-модули (KMM) и использовать View Binding для устранения дублирования findViewById.
В iOS DRY достигается через протоколы с default implementation, общие сетевые конфигурации (NetworkConfig), фабрики ячеек UICollectionView и SPM-пакеты с общей бизнес-логикой. Extensions стандартных типов (Date, String, URL) сокращают дублирование форматирования и валидации.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также