# SRP - Reguła Jednej Odpowiedzialności

W świecie programowania obiektowego, zasady SOLID to fundament, na którym budujemy czysty, skalowalny i łatwy w utrzymaniu kod. Dziś przyjrzymy się pierwszej i być może najważniejszej z nich: **Regule Jednej Odpowiedzialności** (Single Responsibility Principle - SRP). Co tak naprawdę oznacza i dlaczego jej ignorowanie może prowadzić do poważnych problemów w projekcie?

### Czym jest Reguła Jednej Odpowiedzialności?

Zasada sformułowana przez Roberta C. Martina (znanego jako "Wujek Bob") jest zwodniczo prosta:

> **Klasa powinna mieć tylko jeden powód do zmiany.**

Co to oznacza w praktyce? Każda klasa w Twojej aplikacji powinna mieć jedną, jasno zdefiniowaną odpowiedzialność. Nie chodzi o to, by klasa miała tylko jedną metodę, ale o to, by wszystkie jej metody i pola były spójne i służyły jednemu, konkretnemu celowi. Jeśli możesz wskazać więcej niż jeden powód, dla którego klasa mogłaby wymagać modyfikacji w przyszłości, to znak, że łamiesz SRP.

Pomyśl o tym jak o szwajcarskim scyzoryku. Jest świetny na biwaku, ale w kuchni do krojenia chleba użyjesz noża do chleba, a do otwierania wina – korkociągu. Każde z tych narzędzi jest wyspecjalizowane i robi jedną rzecz naprawdę dobrze. Tak samo powinno być z Twoimi klasami.

### Przykład: Jak złamać zasadę SRP?

Wyobraźmy sobie klasę, która ma zarządzać danymi pracownika. Na pierwszy rzut oka, poniższy kod może wydawać się w porządku.

**Przykład złego kodu (naruszenie SRP):**

```java
public class Pracownik {
    private String imie;
    private String stanowisko;
    private double pensja;

    public Pracownik(String imie, String stanowisko, double pensja) {
        this.imie = imie;
        this.stanowisko = stanowisko;
        this.pensja = pensja;
    }

    // Odpowiedzialność 1: Logika biznesowa
    public void awansuj(String noweStanowisko, double podwyzka) {
        this.stanowisko = noweStanowisko;
        this.pensja += podwyzka;
        System.out.println(imie + " awansował na stanowisko: " + noweStanowisko);
    }

    // Odpowiedzialność 2: Zapis do bazy danych
    public void zapiszWBazieDanych() {
        // Logika łącząca się z bazą danych i zapisująca obiekt...
        System.out.println("Zapisano pracownika " + this.imie + " w bazie danych.");
    }

    // Odpowiedzialność 3: Generowanie raportu
    public String generujRaportXML() {
        // Logika formatująca dane pracownika do formatu XML...
        return "<pracownik><imie>" + this.imie + "</imie><stanowisko>" + this.stanowisko + "</stanowisko></pracownik>";
    }
}
```

Ta klasa ma aż **trzy różne powody do zmiany**:

1. **Zmiana logiki biznesowej:** Zmieniają się zasady awansowania pracowników.
    
2. **Zmiana technologii bazy danych:** Przechodzimy z SQL na NoSQL i metoda `zapiszWBazieDanych()` musi zostać napisana od nowa.
    
3. **Zmiana formatu raportowania:** Dział finansowy prosi o raporty w formacie JSON zamiast XML.
    

Mieszanie tych trzech różnych odpowiedzialności w jednej klasie to prosta droga do kłopotów.

### Konsekwencje braku stosowania SRP

* **Trudności w utrzymaniu (wysokie sprzężenie):** Zmiana w jednym obszarze (np. formacie raportu) wymusza modyfikację i ponowne testowanie całej klasy, co zwiększa ryzyko wprowadzenia błędu w zupełnie niepowiązanym module (np. logice biznesowej).
    
* **Niska czytelność:** Klasa staje się "boskim obiektem" (God Object), który robi wszystko. Nowym programistom w zespole trudno będzie zrozumieć jej przeznaczenie.
    
* **Problemy z testowaniem:** Jak napisać prosty test jednostkowy dla logiki awansu, skoro klasa ma zależności związane z bazą danych? Trzeba tworzyć skomplikowane mocki i zaślepki, co utrudnia proces testowania.
    
* **Ograniczona reużywalność:** Chciałbyś ponownie użyć logiki do generowania raportów w innym miejscu aplikacji? Niestety, ciągniesz za sobą cały bagaż związany z danymi pracownika i logiką biznesową.
    

### Jak to naprawić? Refaktoryzacja z SRP

Zgodnie z zasadą jednej odpowiedzialności, powinniśmy rozbić naszą klasę `Pracownik` na mniejsze, wyspecjalizowane klasy.

**1\. Klasa** `Pracownik` (tylko dane i logika biznesowa) Ta klasa przechowuje już tylko stan obiektu i metody operujące na tym stanie.

```java
public class Pracownik {
    private String imie;
    private String stanowisko;
    private double pensja;

    public Pracownik(String imie, String stanowisko, double pensja) {
        this.imie = imie;
        this.stanowisko = stanowisko;
        this.pensja = pensja;
    }

    public void awansuj(String noweStanowisko, double podwyzka) {
        this.stanowisko = noweStanowisko;
        this.pensja += podwyzka;
    }

}
```

**Powód do zmiany:** Tylko zmiana w logice biznesowej dotyczącej pracownika.

**2\. Klasa** `PracownikRepository` (odpowiedzialność: persystencja) Ta klasa zajmuje się wyłącznie komunikacją z bazą danych.

```java
public class PracownikRepository {
    public void zapisz(Pracownik pracownik) {
        // Logika zapisu do bazy danych...
        System.out.println("Zapisano pracownika " + pracownik.getImie() + " w bazie danych.");
    }
}
```

**Powód do zmiany:** Tylko zmiana technologii lub sposobu zapisu danych.

**3\. Klasa** `RaportGenerator` (odpowiedzialność: raportowanie) Jej jedynym zadaniem jest tworzenie raportów na podstawie danych pracownika.

```java
public class RaportGenerator {
    public String generujRaportXML(Pracownik pracownik) {
        return "<pracownik><imie>" + pracownik.getImie() + "</imie><stanowisko>" + pracownik.getStanowisko() + "</stanowisko></pracownik>";
    }
    
    public String generujRaportJSON(Pracownik pracownik) {
        // Logika formatująca do JSON...
        return "{\"pracownik\":{\"imie\":\"" + pracownik.getImie() + "\",\"stanowisko\":\"" + pracownik.getStanowisko() + "\"}}";
    }
}
```

**Powód do zmiany:** Tylko zmiana formatu lub zawartości raportu.

Dzięki takiemu podejściu każda klasa ma **jedną, klarowną odpowiedzialność**. Zmiana wymagań w jednym obszarze dotyka tylko jednej, małej klasy. Kod staje się prostszy do zrozumienia, testowania i ponownego użycia.

### Podsumowanie

Reguła Jednej Odpowiedzialności to nie tylko akademicka zasada, ale praktyczne narzędzie, które pomaga pisać lepszy kod. Następnym razem, gdy będziesz tworzyć nową klasę lub modyfikować istniejącą, zadaj sobie pytanie: **"Ile powodów do zmiany ma ta klasa?"**. Jeśli odpowiedź brzmi "więcej niż jeden", wiesz już, co robić. Stosowanie SRP to inwestycja, która zwraca się z nawiązką w postaci czystszego i łatwiejszego w utrzymaniu oprogramowania.
