Принцип DRY (don’t repeat yourself — не повторяйтесь) является фундаментальным в разработке программного обеспечения. Он предостерегает от дублирования кода и поощряет его многократное использование. Принцип гласит: каждая информационная единица системы должна иметь единственное, однозначное представление. Проще говоря, одна и та же логика или информация не должна повторяться в нескольких местах кодовой базы.
Принцип DRY призывает разработчиков абстрагировать общую функциональность в многократно используемые компоненты, такие как функции, классы или модули. Это способствует созданию легко поддерживаемого кода, который предполагает внесение изменений или обновлений только в одном месте. Такой код более понятен и удобочитаем, менее подвержен риску возникновения несоответствий и ошибок.
Соблюдение принципа DRY обеспечивает развитие лучших практик проектирования программного обеспечения, таких как модульность, инкапсуляция и абстракция. Это помогает создавать более чистые и удобные в обслуживании кодовые базы, которые легче расширять, отлаживать и совместно работать над ними.
На что нацелен принцип DRY?
- Удобство обслуживания кода. Дублирование в коде приводит к увеличению накладных расходов на обслуживание. Когда одна и та же логика кода копируется и вставляется в несколько мест, любые модификации или обновления этой логики требуют изменений в нескольких местах, что увеличивает риск возникновения несоответствий и ошибок. Соблюдение принципа DRY в Kotlin гарантирует внесение изменения только в одном месте, что упрощает сопровождение кода.
- Уменьшение количества ошибок и недочетов. Дублирование кода повышает вероятность появления багов и ошибок. Если ошибка исправлена в одном экземпляре дублированного кода, но не исправлена в других, возникают несоответствия, приводящие к неожиданному поведению. Централизация логики кода Kotlin, соответствующего принципу DRY, минимизирует вероятность появления ошибок из-за несоответствий.
- Повышение удобочитаемости и ясности. Дублирование кода ухудшает его читаемость и затрудняет понимание. Когда одна и та же логика разбросана по всей кодовой базе, становится сложно понять общую функциональность и назначение системы. Следуя принципу DRY и консолидируя логику, код Kotlin становится более удобочитаемым и ясным.
- Способствует модульности и многократному использованию. Следование принципу DRY поощряет создание модульных и многократно используемых компонентов. Вместо повторения одной и той же логики в нескольких местах, разработчикам Kotlin рекомендуется выделять общую функциональность в отдельные модули, функции или классы. Это способствует созданию модульной архитектуры, предполагающей неоднократное использование компонентов во всей кодовой базе.
Примеры применения принципа DRY в Kotlin
1. Выделение общей функциональности в функции или функции расширения
// Несоблюдение принципа DRY: функции для определенных форм fun calculateCircleArea(radius: Double): Double { return Math.PI * radius * radius } fun calculateRectangleArea(width: Double, height: Double): Double { return width * height } // Соблюдение принципа DRY: общая функция для вычисления площади fun calculateArea(shape: Shape): Double { return when (shape) { is Circle -> Math.PI * shape.radius * shape.radius is Rectangle -> shape.width * shape.height } }
Вместо использования отдельных функций для вычисления площади различных фигур, создаем единую функцию calculateArea
, которая принимает на вход объект Shape
и вычисляет его площадь в зависимости от типа. Такой подход исключает дублирование кода и способствует его многократному использованию.
2. Использование функций высшего порядка
// Несоблюдение принципа DRY fun applyOperationTwice(value: Int, operation: (Int) -> Int): Int { return operation(operation(value)) } // Соблюдение принципа DRY fun applyOperationTwice(value: Int, operation: (Int) -> Int): Int { return operation(value).let(operation) }
В функции applyOperationTwice
применяется принцип DRY с использованием функции let
для вызова операции по результату первого вызова. Такой подход улучшает читаемость и сохраняет последовательность в применении операций.
3. Совместное использование констант
// Без соблюдения принципа DRY: константы объявляются глобально const val MAX_RETRIES = 3 const val TIMEOUT = 5000 // Соблюдение принципа DRY: константы сгруппированы в объекте object Constants { const val MAX_RETRIES = 3 const val TIMEOUT = 5000 }
Вместо объявления констант глобально, применяем принцип DRY путем группировки связанных констант внутри объекта. Такой подход логически упорядочивает константы и предотвращает загрязнение пространства имен.
4. Многократно используемые структуры данных
// Без соблюдения принципа DRY: отдельные классы для каждой формы class Rectangle(val width: Double, val height: Double) class Circle(val radius: Double) // Соблюдение принципа DRY: общий базовый класс и классы данных sealed class Shape data class Rectangle(val width: Double, val height: Double) : Shape() data class Circle(val radius: Double) : Shape()
Принцип DRY применяется путем определения общего базового класса Shape
и создания на его основе конкретных фигур, таких как Rectangle
и Circle
. Этот подход способствует многократному использованию кода и поддерживает последовательность в представлении фигур.
5. Совместное использование бизнес-логики
Без соблюдения принципа DRY: дублирование логики в нескольких классах Manager class UserManager(private val userRepository: UserRepository) { fun addUser(user: User) { if (userRepository.getUserById(user.id) == null) { userRepository.addUser(user) } } } class ProductService(private val productRepository: ProductRepository) { fun addProduct(product: Product) { if (productRepository.getProductById(product.id) == null) { productRepository.addProduct(product) } } } // Соблюдение принципа DRY: класс BaseManager для общей логики class BaseManager<T>(private val repository: BaseRepository<T>) { fun addItem(item: T) { if (repository.getItemById(item.id) == null) { repository.addItem(item) } } } class UserManager(private val userRepository: UserRepository) : BaseManager<User>(userRepository) class ProductService(private val productRepository: ProductRepository) : BaseManager<Product>(productRepository)
Применение принципа DRY обеспечивает создание общего класса BaseManager
, который инкапсулирует общую бизнес-логику для добавления элементов. Специальные классы менеджеров, такие как UserManager
и ProductService
, наследуют от BaseManager
повторное использование этой общей логики, что снижает риск дублирования кода и способствует согласованности.
Рекомендации по корректному использованию DRY
- Избегайте преждевременных абстракций. Очень важно соблюдать баланс между дублированием кода и абстракцией. Иногда преждевременное извлечение общей функциональности может привести к созданию слишком сложных абстракций, которые трудно понять и поддерживать. Абстрагируйте код только тогда, когда он действительно пригоден для повторного использования.
- Подходите ответственно к именованию. При извлечении кода в компоненты многократного использования выбирайте описательные и осмысленные имена для функций, классов или модулей. Четкое именование гарантируют, что смысл и назначение кода будут легко понятны другим разработчикам.
- Сохраняйте целостность функций. Создавая многократно используемые функции или классы, убедитесь, что они несут единственную ответственность и выполняют четко определенную задачу. Смешивание несвязанных функций в рамках одной функции или класса может привести к путанице и нарушению принципа единственной ответственности (SRP).
- Учитывайте необходимость компромиссов. Хотя дублирование кода должно быть сведено к минимуму, иногда оно приемлемо, если приводит к более четкому и удобному для сопровождения коду. Рассмотрите компромиссы между DRY и другими принципами проектирования программного обеспечения, такими как простота и ясность.
- Не пренебрегайте документацией и комментариями. Документируйте назначение и применение многократно используемых компонентов, чтобы помочь другим разработчикам понять, как правильно их использовать. Кроме того, прибегайте к комментированию, чтобы объяснить сложную или неочевидную логику в коде.
Читайте также:
- Как Ktlint облегчает жизнь разработчикам
- Корутины в Kotlin: топ-50 вопросов для собеседования с Android-разработчиками в 2024 году
- Важные вопросы для собеседования по корутинам для опытных разработчиков Android
Читайте нас в Telegram, VK и Дзен
Перевод статьи Rizwanul Haque: The DRY Principle in Kotlin: Enhancing Code Quality and Maintainability