Single Responsibility Principle
Single Responsibility Principle – SRP
Zasada pojedynczej odpowiedzialności mówi, że każda klasa, moduł lub funkcja powinny mieć tylko jedno, jasno określone zadanie. W praktyce oznacza to, że każdy komponent w aplikacji powinien odpowiadać tylko za jeden aspekt funkcjonalności systemu.
Dlaczego to jest ważne:
Łatwiejsze utrzymanie: Kod, który przestrzega zasady jednej odpowiedzialności, jest bardziej przejrzysty i łatwiejszy do zrozumienia. Gdy klasa robi tylko jedną rzecz, jej odpowiedzialność jest jednoznaczna, co ułatwia debugowanie i wprowadzanie zmian.
Mniejsza podatność na błędy: Gdy jedna klasa jest odpowiedzialna za zbyt wiele zadań, każda zmiana może wpłynąć na wiele funkcji naraz, co zwiększa ryzyko wprowadzenia błędów. Podział obowiązków redukuje to ryzyko.
Łatwiejsze testowanie: Gdy klasa wykonuje jedno zadanie, testy jednostkowe są bardziej specyficzne i skuteczniejsze. Można testować każdą funkcjonalność oddzielnie.
Przykład z życia:
Remont domu
Podczas remontu domu, różne zadania wymagają różnych specjalistów. Przykładowe zadania to:
- Instalacja hydrauliczna: Montaż rur, umywalek, toalet, itp.
- Instalacja elektryczna: Montaż przewodów, gniazdek, przełączników, itp.
- Malowanie ścian: Przygotowanie powierzchni, malowanie, wykończenie.
- Montaż podłóg: Instalacja paneli, desek, kafli itp.
- Remont kuchni: Wymiana szafek, blatu, AGD itp.
Każde z tych zadań wymaga innej wiedzy i umiejętności, dlatego w rzeczywistości zatrudniasz różnych specjalistów do każdego z tych zadań:
- Hydraulik: Specjalizuje się w instalacjach wodno-kanalizacyjnych. Odpowiada za montaż rur, kranów, umywalek i systemów grzewczych.
- Elektryk: Zajmuje się instalacją elektryczną. Montuje przewody, gniazdka, oświetlenie i systemy zabezpieczeń.
- Malacz: Specjalizuje się w malowaniu ścian. Przygotowuje powierzchnie, nakłada farby i wykończenia.
- Specjalista od podłóg: Montuje różne typy podłóg, w tym panele, kafle i deski.
- Stolarz kuchenny: Zajmuje się montażem mebli kuchennych i urządzeń.
Przykład programu przed użyciem zasady SRP:
Wyobraź sobie klasę UserManager
, która zarządza użytkownikami i ma wiele zadań:
import java.util.ArrayList;
import java.util.List;
// Klasa łamiąca zasadę SRP
public class UserManager {
private List<String> users = new ArrayList<>();
// Dodaje użytkownika do listy
public void addUser(String user) {
users.add(user);
}
// Zapisuje listę użytkowników do pliku
public void saveUsersToFile() {
try (FileWriter writer = new FileWriter("users.txt")) {
for (String user : users) {
writer.write(user + System.lineSeparator());
}
} catch (IOException e) {
e.printStackTrace();
}
}
// Wysyła powiadomienie do wszystkich użytkowników
public void sendNotification(String message) {
for (String user : users) {
System.out.println("Wysyłanie wiadomości do " + user + ": " + message);
}
}
public static void main(String[] args) {
UserManager manager = new UserManager();
manager.addUser("Alice");
manager.addUser("Bob");
manager.saveUsersToFile();
manager.sendNotification("Witajcie w naszej aplikacji!");
}
}
W powyższym kodzie klasa UserManager
ma trzy różne odpowiedzialności:
- Dodawanie użytkowników.
- Zapisywanie użytkowników do pliku.
- Wysyłanie powiadomień do użytkowników.
Poprawiony kod zgodny z zasadą SRP
Rozdzielmy odpowiedzialności na osobne klasy:
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
// Klasa odpowiedzialna za zarządzanie użytkownikami
public class UserManager {
private List<String> users = new ArrayList<>();
public void addUser(String user) {
users.add(user);
}
public List<String> getUsers() {
return users;
}
}
// Klasa odpowiedzialna za zapisywanie użytkowników do pliku
public class UserFileSaver {
public void saveUsersToFile(List<String> users, String filename) {
try (FileWriter writer = new FileWriter(filename)) {
for (String user : users) {
writer.write(user + System.lineSeparator());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
// Klasa odpowiedzialna za wysyłanie powiadomień do użytkowników
public class UserNotifier {
public void sendNotification(List<String> users, String message) {
for (String user : users) {
System.out.println("Wysyłanie wiadomości do " + user + ": " + message);
}
}
}
public class Main {
public static void main(String[] args) {
UserManager userManager = new UserManager();
userManager.addUser("Alice");
userManager.addUser("Bob");
UserFileSaver fileSaver = new UserFileSaver();
fileSaver.saveUsersToFile(userManager.getUsers(), "users.txt");
UserNotifier notifier = new UserNotifier();
notifier.sendNotification(userManager.getUsers(), "Witajcie w naszej aplikacji!");
}
}
Analiza poprawionego kodu
TaskManager
: Klasa ta jest odpowiedzialna tylko za zarządzanie zadaniami – dodawanie ich do listy i udostępnianie listy zadań innym klasom.ReportGenerator
: Klasa ta zajmuje się tylko generowaniem raportu na podstawie listy zadań. Przyjmuje listę zadań i zwraca sformatowany raport.ReportSaver
: Klasa ta odpowiada za zapisywanie raportu do pliku. Przyjmuje raport i nazwę pliku, do którego ma być zapisany.