Czym jest Zasada Pojedynczej Odpowiedzialności?
Najprościej mówiąc, zasada ta stanowi, że każda klasa powinna mieć tylko jedną odpowiedzialność, a co za tym idzie – tylko jeden powód do zmiany.
Co to oznacza w praktyce? Jeśli Twoja klasa zajmuje się jednocześnie walidacją danych, zapisem do bazy i wysyłką maili, to ma co najmniej trzy powody do modyfikacji. Zmiana logiki walidacji, zmiana schematu bazy danych lub zmiana serwera SMTP – każda z tych sytuacji zmusi Cię do edycji tej samej klasy. To prosta droga do tworzenia tzw. klas-bogów (God Objects), które wiedzą o wszystkim i robią wszystko, stając się koszmarem w utrzymaniu i testowaniu.
Kluczowe aspekty SRP
Stosowanie się do tej zasady przynosi wymierne korzyści:
- Zwiększona czytelność: Małe, skoncentrowane klasy są łatwiejsze do zrozumienia.
- Łatwiejsze testowanie: Prościej jest pisać testy jednostkowe dla klasy, która realizuje jedno konkretne zadanie.
- Mniejsze sprzężenie (coupling): Klasy są od siebie bardziej niezależne.
- Większa reużywalność: Klasę odpowiedzialną za logowanie możesz wykorzystać w wielu miejscach w aplikacji.
- Prostsze utrzymanie: Gdy musisz coś zmienić, modyfikujesz tylko jeden, dobrze zdefiniowany fragment kodu.
Przykład naruszenia zasady
Zobaczmy, jak w praktyce wygląda złamanie SRP. Wyobraźmy sobie klasę Assistant, która ma za zadanie przechowywać wiek pracownika, obliczać lata do emerytury i logować informacje na konsolę.
Diagram UML

Kod
Oto przykład klasy Assistant, która robi zbyt wiele:
package assistant;
public class Assistant {
public int retirementAge = 65;
private int age;
public Assistant(int age){
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void handleEmployee(){
System.out.println("Logging data..."); // Odpowiedzialność: logowanie
System.out.println(this.retirementAge - this.age); // Odpowiedzialność: obliczenia biznesowe
}
}import assistant.Assistant;
public class Main {
public static void main(String[] args) {
Assistant assistant = new Assistant(30);
assistant.handleEmployee();
}
}Analiza i Problem
W powyższym kodzie klasa Assistant ma trzy odpowiedzialności:
- Zarządzanie danymi: Przechowuje i udostępnia wiek (
age). - Logowanie: Wypisuje komunikat “Logging data…”.
- Obliczenia biznesowe: Oblicza lata pozostałe do emerytury.
Metoda handleEmployee miesza logowanie z logiką biznesową. Problem? Jeśli zmieni się sposób logowania (np. z konsoli na zapis do pliku), będziemy musieli modyfikować klasę, która zawiera również logikę finansową. Podobnie, zmiana wieku emerytalnego (retirementAge) wymusi edycję klasy, która zajmuje się logowaniem. Klasa ma więcej niż jeden powód do zmiany, co jest bezpośrednim naruszeniem SRP.
Poprawne zastosowanie zasady
Aby naprawić nasz kod, musimy wydzielić poszczególne odpowiedzialności do osobnych, wyspecjalizowanych klas.
FinancesAssistant– będzie odpowiedzialna tylko za obliczenia finansowe.Logger– jej jedynym zadaniem będzie logowanie informacji.AssistantRefactor– będzie pełnić rolę koordynatora, delegując zadania do odpowiednich klas.
Diagram UML

Kod po refaktoryzacji
Klasa do obliczeń:
package assistant;
public class FinancesAssistant {
private int retirementAge = 65;
private int age;
public FinancesAssistant(int age) {
this.age = age;
}
public void calculate(){
System.out.println(this.retirementAge - this.age);
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}Klasa do logowania:
package assistant;
public class Logger {
public void log(){
System.out.println("Logging data...");
}
}Klasa koordynująca:
package assistant;
public class AssistantRefactor {
private FinancesAssistant financesAssistant;
private Logger logger;
public AssistantRefactor(int age){
this.financesAssistant = new FinancesAssistant(age);
this.logger = new Logger();
}
public void handleEmployee(){
this.logger.log();
this.financesAssistant.calculate();
}
}Analiza
Teraz każda klasa ma tylko jedną, jasno zdefiniowaną odpowiedzialność.
FinancesAssistantma jeden powód do zmiany: modyfikacja logiki obliczeń emerytalnych.Loggerma jeden powód do zmiany: modyfikacja sposobu logowania.AssistantRefactorkoordynuje pracę, ale sama nie implementuje logiki biznesowej ani logowania. Jej odpowiedzialnością jest orkiestracja.
Porównanie przykładów
| Cecha | Przed refaktoryzacją (Assistant) | Po refaktoryzacji (AssistantRefactor i spółka) |
| Odpowiedzialności | Wiele (dane, logowanie, obliczenia) | Każda klasa ma jedną, jasno określoną |
| Sprzężenie | Wysokie – logika biznesowa jest powiązana z logowaniem | Niskie – klasy są niezależne |
| Testowalność | Trudniejsza – trzeba testować wiele funkcji naraz | Łatwiejsza – można testować każdą klasę w izolacji |
| Podatność na zmiany | Wysoka – zmiana w logowaniu może zepsuć obliczenia | Niska – zmiana dotyka tylko jednej, konkretnej klasy |
Podsumowanie
Zasada Pojedynczej Odpowiedzialności to fundament, na którym buduje się stabilne i skalowalne aplikacje. Choć na początku może się wydawać, że tworzenie wielu małych klas to nadmiarowa praca, w dłuższej perspektywie jest to inwestycja, która zwraca się z nawiązką w postaci łatwiejszego utrzymania, testowania i rozwijania kodu. Pamiętaj: jedna klasa, jeden powód do zmiany!
Bibliografia
- Martin, R. C. (2008). Clean Code: A Handbook of Agile Software Craftsmanship. Prentice Hall.
- Martin, R. C. (2003). Agile Software Development, Principles, Patterns, and Practices. Pearson.