SRP: що це, принцип єдиної відповідальності в розробці

Автор: IT Sectr Опубліковано: 2026-05-11 Час читання: 9 хв

SRP (Single Responsibility Principle) — перший принцип SOLID, який визначає: кожен клас або модуль повинен мати рівно одну причину для зміни. Цей принцип був сформульований Робертом Мартіном в книзі Clean Architecture (2017) і став фундаментом модульного проектування. За даними цієї книги, застосування SRP безпосередньо знижує зв’язаність компонентів та усунає каскадні зміни при доопрацюванні функціональності.

Головне

  • SRP — перший принцип SOLID, що вимагає однієї відповідальності на клас
  • Причина зміни — єдиний критерій виділення відповідальності в модуль
  • Порушення SRP призводить до зв’язаного коду, який складно тестувати та розширювати
  • Застосування принципу спрощує рефакторинг та зменшує ризик регресійних помилок
  • SRP в мобільній розробці допомагає розділяти логіку UI, бізнес-правила та роботу з даними

Що таке SRP (Single Responsibility Principle)?

SRP (Single Responsibility Principle) — принцип єдиної відповідальності, який голосить: кожен клас або модуль повинен мати рівно одну причину для зміни. Це не означає, що клас повинен виконувати рівно одну операцію. Йдеться про групу пов’язаних дій, об’єднаних однією відповідальністю перед одним актором.

Роберт Мартін переформулював SRP в термінах акторів: клас повинен змінюватися лише на запит однієє зацікавленої сторони або однієї групи людей. Якщо два різні актори вимагають змін в одному й тому самому класі, відповідальність розділена неправильно.

Наприклад, клас Employee, який одночасно розраховує зарплату (запит бухгалтерії) та формує звіт (запит менеджменту), порушує SRP. Зміна правил розрахунку може вплинути на формування звіту і навпаки.

Формальне визначення SRP

Модуль повинен мати одну і тільки одну причину для зміни. Причина зміни визначається актором — особою або системою, яка ініціює вимогу. Якщо вимоги від різних акторів призводять до зміни одного модуля, цей модуль порушує SRP.

Концепція актора робить SRP практичним інструментом архітектурного аналізу, а не абстрактною рекомендацією. При проектуванні системи достатньо запитати: «Хто проситиме змінити цей код?» — якщо відповідь містить більше однієї зацікавленої сторони, відповідальність варто розділити.

Як працює принцип єдиної відповідальності

Єдина відповідальність реалізується через групування методів, які змінюються з однієї причини. Клас стає «точкою збору» пов’язаної логіки, а не «швейцарським ножем» на всі випадки. Це спрощує розуміння коду: розробник бачить клас і одразу розуміє його призначення.

Механізм роботи SRP базується на правилі однієї осі зміни. Якщо функціональність може змінюватися з незалежних причин, вона повинна бути винесена в окремі класи. Зв’язки між цими класами будуються через композицію або делегування.

Порушення SRP проявляється в «класах-богах» (God Objects), які містять десятки методів, що працюють з різними даними. Такий клас складно тестувати — тестування одного методу вимагає налаштування середовища для всіх інших. Зміна однієї відповідальності може зламати іншу, що робить код хрупким.

На практиці SRP допомагає розробникам відповідати на питання «де знаходиться цей код?». Якщо кожна відповідальність виділена в свій клас, пошук потрібного файлу займає секунди. В Android-проекті з архітектурою MVVM це означає, що UserViewModel відповідає лише за стан екрану користувача, а UserRepository — за отримання даних. Розробник, який шукає логіку кешування, йде в UserCacheRepository, а не в ViewModel. Така організація коду прискорює вхідження нових членів команди та зменшує кількість помилок під час рефакторингу.

Чому SRP важливий в мобільній розробці

Мобільна розробка пред’являє особливі вимоги до модульності коду. Android Fragment або iOS ViewController часто стають «точками притягування» логіки: обробка натискань, виклики API, аналіз відповіді, оновлення UI — все в одному класі. SRP вимагає розділити ці обов’язки.

В Android-архітектурі SRP закладений в рекомендації Google щодо Jetpack: ViewModel відповідає за стан екрану, Repository — за дані, UseCase — за бізнес-логіку. Кожен компонент має одну причину для зміни. В iOS-розробці патерни MVVM та Coordinator дотримуються тієї самої логіки.

Дотримання SRP в мобільних проектах дає вимірювані переваги: зменшення розміру класів на 40-60%, скорочення часу на код-ревю та зменшення кількості регресійних помилок при додаванні нової функціональності. Ізольовані модулі легше покривати юніт-тестами та повторно використовувати в інших екранах.

Вплив SRP на тестування

Юніт-тестування класів, що дотримуються SRP, потребує менше mock-об’єктів та налаштувань. Якщо клас має одну відповідальність, його залежності обмежені. Тест перевіряє одну поведінку, а не комбінацію кількох непов’язаних сценаріїв.

За даними звіту Google Testing Blog (2023), класи з єдиною відповідальністю показують на 35% більше покриття тестами порівняно з класами-агрегаторами. Розробники охочіше пишуть тести для невеликих, зрозумілих модулів.

Приклади SRP в Android та iOS

Розглянемо типовий Android-клас, який порушує SRP — він завантажує дані, аналізує відповідь та оновлює UI. Після рефакторингу кожна відповідальність виділена в окремий компонент.

kotlin
// Порушення SRP: один клас робить усе
class BadUserProfileActivity {
    fun loadUser(userId: Int) {
        // HTTP запит
        // Парсинг JSON
        // Оновлення UI
        // Збереження в БД
    }
}

// Після застосування SRP
class UserRepository {
    fun getUser(userId: Int): User
}

class UserViewModel {
    private val repo: UserRepository
    fun loadUser(userId: Int) { }
}

class UserProfileFragment {
    fun render(user: User) { }
}

Аналогічний приклад на iOS Swift з розділенням мережевого рівня та відображення:

swift
// Порушення SRP: ViewController керує даними та UI
class BadProfileViewController: UIViewController {
    func viewDidLoad() {
        // URLSession запит
        // Декодування JSON
        // Оновлення label
    }
}

// Після застосування SRP
protocol UserServiceProtocol {
    func fetchUser(id: Int) async throws -> User
}

class ProfileViewModel {
    private let service: UserServiceProtocol
    func loadProfile(id: Int) { }
}

class ProfileViewController: UIViewController {
    func display(user: User) { }
}

Рефакторинг SRP не ускладнює архітектуру — він перерозподіляє відповідальність. Кількість коду може навіть зменшитися за рахунок усунення дублювань. Кожен новий клас має чітке призначення і може розроблятися незалежно.

Композиція як альтернатива успадкуванню

Композиція допомагає дотримуватися SRP там, де успадкування створює зайві зв’язки. Замість суперкласу з десятками методів, підклас отримує набір спеціалізованих об’єктів через конструктор. Кожен об’єкт відповідає за свою функціональність.

В Android-розробці патерн Decorator дозволяє додавати відповідальності без зміни вихідного класу. В iOS ланцюг Middleware в мережевому рівні розділяє логування, кешування та автентифікацію на окремі модулі.

Типові порушення SRP та їхні наслідки

Найчастіше порушення — «God Class»: клас, який керує базою даних, надсилає сповіщення, генерує звіти та обробляє введення користувача. Такий клас стає вузьким місцем проекту: будь-яка зміна вимагає повного регресійного тестування.

В мобільній розробці до порушення SRP веде змішування бізнес-логіки та UI-логіки в Activity, Fragment або ViewController. Коли метод onClickListener одночасно валідує дані, викликає API та оновлює видимість кнопок — це пряме порушення принципу єдиної відповідальності.

Наслідки порушення SRP включають: складність паралельної розробки (конфлікти в одному файлі), утруднене юніт-тестування, високу вартість внесення змін та зниження читабельності коду. Проекти з систематичним порушенням SRP потребують в 2-3 рази більше часу на додавання нової функціональності.

Індикатори порушення SRP в коді

Визначити порушення SRP можна за непрямими ознаками: клас містить понад 200 рядків, імпортує модулі з різних рівнів додатка (UI + network + database), має понад 5 публічних методів різної тематики. Метрика зв’язності (cohesion) — статистичний показник: низька зв’язність методів всередині класу вказує на порушення SRP.

Для виявлення SRP-порушень корисно використовувати інструменти статичного аналізу: для Android — Detekt з правилом TooManyFunctions, для iOS — SwiftLint з правилом file_length. Ці утиліти підсвічують класи, що перевищують порогові значення розміру та складності.

Рефакторинг класів, що порушують SRP, виконується через Extract Class або Extract Delegate: група пов’язаних методів виноситься в окремий клас, а вихідний клас делегує їм виклики. Поступове застосування таких рефакторингів перетворює «God Class» в набір слабкозв’язаних модулів, кожен з однією відповідальністю. Такий підхід дозволяє покращувати архітектуру без зупинки розробки — рефакторинг виконується —теративно, по одному модулю за раз.

Часто задавані питання

Чи означає SRP, що клас повинен містити один метод?

Ні. SRP не про кількість методів, а про кількість причин для зміни. Клас може мати десяток методів, якщо всі вони обслуговують одну відповідальність перед одним актором. Один метод — це інша крайність, яка веде до надмірного дрібнення коду.

Чим SRP відрізняється від принципу єдиного обов’язку?

Це один і той самий принцип. Single Responsibility Principle перекладається — як «єдина відповідальність», так — як «єдиний обов’язок». Термін «відповідальність» точніше відображає суть: йдеться про відповідальність перед актором, а не про технічну функцію.

Як SRP пов’язаний з патерном Repository?

Repository — прямий результат застосування SRP до рівня даних. Замість того, щоб розмазувати логіку доступу до даних по ViewModel або UseCase, Repository бере на себе єдину відповідальність: надання даних з абстракцією джерела. Це класична реалізація SRP в мобільній архітектурі.

Чи може клас з SRP мати залежності від інших класів?

Так, SRP не забороняє залежності. Клас з однією відповідальністю може делегувати частину роботи іншим класам через композицію. Важливо, щоб ці делеговані завдання були частиною тієї самої відповідальності, а не незалежною причиною для зміни.

Як перевірити, чи дотримується клас SRP?

Задайте питання: «Які актори можуть вимагати змін цього класу?» Якщо відповідь містить більше одного актора — SRP порушено. Додатково: спробуйте описати призначення класу одним реченням без сполучника «і». Якщо не вдається — клас робить забагато.

Підсумки

  • SRP (Single Responsibility Principle) — перший принцип SOLID, що вимагає однієї причини для зміни класу
  • Причина зміни визначається актором — особою або системою, що ініціює вимогу до модуля
  • Порушення SRP веде до God Class, низької тестованості та високих витрат на зміни
  • В мобільній розробці SRP розділяє UI-логіку, бізнес-логіку та роботу з даними по окремих компонентах
  • Композиція допомагає дотримуватися SRP ефективніше, ніж успадкування, за рахунок делегування спеціалізованим об’єктам
  • Інструменти статичного аналізу (Detekt, SwiftLint) автоматично виявляють потенційні порушення SRP
  • Юніт-тестування класів з SRP потребує менше mock-об’єктів та показує вище покриття коду

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також