SRP: co to jest, zasada jednej odpowiedzialności w programowaniu

Autor: IT Sectr Opublikowano: 2026-05-11 Czas czytania: 9 min

SRP (Single Responsibility Principle) — pierwsza zasada SOLID, która określa: każda klasa lub moduł powinny mieć dokładnie jeden powód do zmiany. Zasada ta została sformułowana przez Roberta Martina w książce Clean Architecture (2017) i stała się fundamentem projektowania modułowego. Według tej książki stosowanie SRP bezpośrednio zmniejsza powiązania między komponentami i eliminuje zmiany kaskadowe przy rozwijaniu funkcjonalności.

Najważniejsze

  • SRP — pierwsza zasada SOLID, wymagająca jednej odpowiedzialności na klasę
  • Przyczyna zmiany — jedyne kryterium wydzielenia odpowiedzialności do modułu
  • Naruszenie SRP prowadzi do silnie powiązanego kodu, trudnego do testowania i rozwijania
  • Stosowanie zasady upraszcza refaktoryzację i zmniejsza ryzyko błędów regresyjnych
  • SRP w programowaniu mobilnym pomaga rozdzielić logikę UI, reguły biznesowe i pracę z danymi

Czym jest SRP (Single Responsibility Principle)?

SRP (Single Responsibility Principle) — zasada jednej odpowiedzialności, która mówi: każda klasa lub moduł powinny mieć dokładnie jeden powód do zmiany. Nie oznacza to, że klasa powinna wykonywać dokładnie jedną operację. Chodzi o grupę powiązanych działań, połączonych jedną odpowiedzialnością wobec jednego aktora.

Robert Martin przeformułował SRP w kategoriach aktorów: klasa powinna zmieniać się tylko na żądanie jednej zainteresowanej osoby lub jednej grupy osób. Jeśli dwóch różnych aktorów wymaga zmiany jednej klasy — odpowiedzialność jest podzielona nieprawidłowo.

Na przykład klasa Employee, która jednocześnie oblicza wynagrodzenie (żądanie księgowości) i generuje raport (żądanie zarządu), narusza SRP. Zmiana zasad obliczania może wpłynąć na generowanie raportu i odwrotnie.

Formalna definicja SRP

Moduł powinien mieć jeden i tylko jeden powód do zmiany. Powód zmiany jest określany przez aktora — osobę lub system inicjujący wymaganie. Jeśli wymagania od różnych aktorów prowadzą do zmiany jednego modułu — moduł narusza SRP.

Koncepcja aktora czyni SRP praktycznym narzędziem analizy architektonicznej, a nie abstrakcyjnym zaleceniem. Przy projektowaniu systemu wystarczy zadać pytanie: „Kto będzie prosił o zmianę tego kodu?” — jeśli odpowiedź zawiera więcej niż jedną zainteresowaną osobę, odpowiedzialność należy podzielić.

Jak działa zasada jednej odpowiedzialności

Jedna odpowiedzialność jest realizowana poprzez grupowanie metod, które zmieniają się z jednego powodu. Klasa staje się „punktem zbornym” powiązanej logiki, a nie „scyzorykiem szwajcarskim” na każdą okazję. Upraszcza to zrozumienie kodu: programista widzi klasę i od razu rozumie jej przeznaczenie.

Mechanizm działania SRP opiera się na zasadzie jednej osi zmian. Jeśli funkcjonalność może zmieniać się z niezależnych przyczyn — powinna być wydzielona do osobnych klas. Powiązania między tymi klasami buduje się poprzez kompozycję lub delegację.

Naruszenie SRP objawia się w „klasach-bogach” (God Objects), które zawierają dziesiątki metod pracujących z różnymi danymi. Taką klasę trudno testować — test jednej metody wymaga konfiguracji środowiska dla wszystkich pozostałych. Zmiana jednej odpowiedzialności może zepsuć inną, co czyni kod kruchym.

W praktyce SRP pomaga programistom odpowiadać na pytanie „gdzie znajduje się ten kod?”. Jeśli każda odpowiedzialność jest wydzielona do swojej klasy, znalezienie potrzebnego pliku zajmuje sekundy. W projekcie Android z architekturą MVVM oznacza to, że UserViewModel odpowiada tylko za stan ekranu użytkownika, a UserRepository — za pobieranie danych. Programista szukający logiki buforowania idzie do UserCacheRepository, a nie do ViewModel. Taka organizacja kodu przyspiesza wdrażanie nowych członków zespołu i zmniejsza liczbę błędów przy refaktoryzacji.

Dlaczego SRP jest ważne w programowaniu mobilnym

Programowanie mobilne stawia szczególne wymagania dotyczące modułowości kodu. Android Fragment lub iOS ViewController często stają się „punktami przyciągania” logiki: obsługa kliknięć, wywołanie API, parsowanie odpowiedzi, aktualizacja UI — wszystko w jednej klasie. SRP wymaga rozdzielenia tych obowiązków.

W architekturze Android SRP jest wbudowane w zalecenia Google dotyczące Jetpack: ViewModel odpowiada za stan ekranu, Repository — za dane, UseCase — za logikę biznesową. Każdy komponent ma jeden powód do zmiany. W programowaniu iOS wzorzec MVVM i Coordinator podążają za tą samą logiką.

Przestrzeganie SRP w projektach mobilnych daje wymierne korzyści: zmniejszenie rozmiaru klas o 40-60%, skrócenie czasu code review i zmniejszenie liczby błędów regresyjnych przy dodawaniu nowej funkcjonalności. Izolowane moduły są łatwiejsze do pokrycia testami jednostkowymi i do ponownego wykorzystania w innych ekranach.

Wpływ SRP na testowanie

Testy jednostkowe klas przestrzegających SRP wymagają mniej mock-objektów i konfiguracji. Jeśli klasa ma jedną odpowiedzialność, jej zależności są ograniczone. Test sprawdza jedno zachowanie, a nie kombinację kilku niepowiązanych scenariuszy.

Według raportu Google Testing Blog (2023), klasy z jedną odpowiedzialnością wykazują o 35% większe pokrycie testami w porównaniu z klasami-agregatorami. Programiści chętniej piszą testy dla małych, zrozumiałych modułów.

Przykłady SRP w Android i iOS

Rozważmy typową klasę Androida, która narusza SRP — ładuje dane, parsuje odpowiedź i aktualizuje UI. Po refaktoryzacji każda odpowiedzialność jest wydzielona do osobnego komponentu.

kotlin
// Naruszenie SRP: jedna klasa robi wszystko
class BadUserProfileActivity {
    fun loadUser(userId: Int) {
        // Żądanie HTTP
        // Parsowanie JSON
        // Aktualizacja UI
        // Zapis do bazy danych
    }
}

// Po zastosowaniu SRP
class UserRepository {
    fun getUser(userId: Int): User
}

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

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

Analogiczny przykład na iOS Swift z podziałem warstwy sieciowej i warstwy prezentacji:

swift
// Naruszenie SRP: ViewController zarządza danymi i UI
class BadProfileViewController: UIViewController {
    func viewDidLoad() {
        // Żądanie URLSession
        // Dekodowanie JSON
        // Aktualizacja etykiety
    }
}

// Po zastosowaniu 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) { }
}

Refaktoryzacja SRP nie komplikuje architektury — redystrybuuje odpowiedzialność. Ilość kodu może nawet się zmniejszyć dzięki wyeliminowaniu powielania. Każda nowa klasa ma jasne przeznaczenie i może być rozwijana niezależnie.

Kompozycja jako alternatywa dla dziedziczenia

Kompozycja pomaga przestrzegać SRP tam, gdzie dziedziczenie tworzy niepotrzebne powiązania. Zamiast superklasy z dziesiątką metod, podklasa otrzymuje zestaw wyspecjalizowanych obiektów przez konstruktor. Każdy obiekt odpowiada za swoją funkcjonalność.

W programowaniu Android wzorzec Decorator pozwala dodawać odpowiedzialności bez zmiany oryginalnej klasy. W iOS łańcuch Middleware w warstwie sieciowej dzieli logowanie, buforowanie i uwierzytelnianie na osobne moduły.

Typowe naruszenia SRP i ich konsekwencje

Najczęstsze naruszenie — „God Class”: klasa, która zarządza bazą danych, wysyła powiadomienia, generuje raporty i przetwarza dane wejściowe użytkownika. Taka klasa staje się wąskim gardłem projektu: każda zmiana wymaga pełnego testowania regresyjnego.

W programowaniu mobilnym do naruszenia SRP prowadzi mieszanie logiki biznesowej i logiki UI w Activity, Fragment lub ViewController. Gdy metoda onClickListener jednocześnie waliduje dane, wywołuje API i aktualizuje widoczność przycisków — to bezpośrednie naruszenie zasady jednej odpowiedzialności.

Konsekwencje naruszenia SRP obejmują: trudność równoległego programowania (konflikty w jednym pliku), utrudnione testy jednostkowe, wysoki koszt wprowadzania zmian i zmniejszoną czytelność kodu. Projekty z systematycznym naruszeniem SRP wymagają 2-3 razy więcej czasu na dodanie nowej funkcjonalności.

Wskaźniki naruszenia SRP w kodzie

Naruszenie SRP można określić na podstawie pośrednich oznak: klasa zawiera ponad 200 linii, importuje moduły z różnych warstw aplikacji (UI + network + database), ma więcej niż 5 publicznych metod o różnej tematyce. Metryka spójności (cohesion) — wskaźnik statystyczny: niska spójność metod wewnątrz klasy wskazuje na naruszenie SRP.

Do wykrywania naruszeń SRP warto używać narzędzi analizy statycznej: dla Android — Detekt z regułą TooManyFunctions, dla iOS — SwiftLint z regułą file_length. Te narzędzia podświetlają klasy przekraczające wartości progowe rozmiaru i złożoności.

Refaktoryzacja klas naruszających SRP odbywa się przez Extract Class lub Extract Delegate: grupa powiązanych metod jest wydzielana do osobnej klasy, a oryginalna klasa deleguje do nich wywołania. Stopniowe stosowanie takich refaktoryzacji przekształca „God Class” w zestaw luźno powiązanych modułów, każdy z jedną odpowiedzialnością. Takie podejście pozwala ulepszać architekturę bez zatrzymywania programowania — refaktoryzacja jest wykonywana iteracyjnie, po jednym module na raz.

Często zadawane pytania

Czy SRP oznacza, że klasa powinna zawierać jedną metodę?

Nie. SRP nie dotyczy liczby metod, lecz liczby powodów do zmiany. Klasa może mieć dziesięć metod, jeśli wszystkie obsługują jedną odpowiedzialność wobec jednego aktora. Jedna metoda to druga skrajność, która prowadzi do nadmiernego dzielenia kodu.

Czym SRP różni się od zasady jednego obowiązku?

To ta sama zasada. Single Responsibility Principle tłumaczy się zarówno jako „jedna odpowiedzialność”, jak i „jeden obowiązek”. Termin „odpowiedzialność” dokładniej oddaje istotę: chodzi o odpowiedzialność wobec aktora, a nie o techniczną funkcję.

Jak SRP wiąże się ze wzorcem Repository?

Repository — bezpośredni rezultat zastosowania SRP do warstwy danych. Zamiast rozrzucać logikę dostępu do danych po ViewModel lub UseCase, Repository przejmuje jedną odpowiedzialność: dostarczanie danych z abstrakcją źródła. To klasyczna implementacja SRP w architekturze mobilnej.

Czy klasa z SRP może mieć zależności od innych klas?

Tak, SRP nie zabrania zależności. Klasa z jedną odpowiedzialnością może delegować część pracy innym klasom poprzez kompozycję. Ważne, aby te delegowane zadania były częścią tej samej odpowiedzialności, a nie samodzielnym powodem do zmiany.

Jak sprawdzić, czy klasa przestrzega SRP?

Zadaj pytanie: „Jacy aktorzy mogą wymagać zmiany tej klasy?” Jeśli odpowiedź zawiera więcej niż jednego aktora — SRP jest naruszone. Dodatkowo: spróbuj opisać przeznaczenie klasy jednym zdaniem bez spójnika „i”. Jeśli się nie udaje — klasa robi zbyt wiele.

Podsumowanie

  • SRP (Single Responsibility Principle) — pierwsza zasada SOLID, wymagająca jednego powodu do zmiany klasy
  • Przyczyna zmiany jest określana przez aktora — osobę lub system inicjujący wymaganie wobec modułu
  • Naruszenie SRP prowadzi do God Class, niskiej testowalności i wysokich kosztów zmian
  • W programowaniu mobilnym SRP dzieli logikę UI, logikę biznesową i pracę z danymi na osobne komponenty
  • Kompozycja pomaga przestrzegać SRP skuteczniej niż dziedziczenie, dzięki delegacji do wyspecjalizowanych obiektów
  • Narzędzia analizy statycznej (Detekt, SwiftLint) automatycznie wykrywają potencjalne naruszenia SRP
  • Testy jednostkowe klas z SRP wymagają mniej mock-objektów i wykazują wyższe pokrycie kodu

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również