Czysty kod i SOLID
SOLID to pięć zasad projektowania obiektowego, które prowadzą do kodu łatwego w zmianie i testowaniu. W połączeniu z podstawami czystego kodu są fundamentem utrzymywalnych serwisów Java w ProfessNet.
S — Single Responsibility
Klasa ma jeden powód do zmiany. Jeśli robi za dużo (logika + zapis + e-mail), rozbij ją.
// źle — serwis robi wszystko
class ReportService {
void generate() { /* dane + format + zapis + wysyłka */ }
}
// dobrze — odpowiedzialności rozdzielone
class ReportBuilder { Report build(ScanResult r) { ... } }
class ReportStorage { void save(Report r) { ... } }
class ReportNotifier { void notify(Report r) { ... } }
O — Open/Closed
Klasy otwarte na rozszerzenie, zamknięte na modyfikację. Nowe warianty
dodajemy przez nowe implementacje, nie przez if/else rosnący w nieskończoność.
interface Exporter { String export(Report r); }
class JsonExporter implements Exporter { /* ... */ }
class CsvExporter implements Exporter { /* ... */ }
// nowy format = nowa klasa, bez ruszania istniejących
L — Liskov Substitution
Podtyp musi działać wszędzie tam, gdzie typ bazowy — bez niespodzianek. Nie nadpisujemy metody tak, by łamała kontrakt (np. rzucała tam, gdzie bazowa nie).
Standard ProfessNet: jeśli podklasa musi rzucić
UnsupportedOperationExceptionz metody bazowej, to znak, że hierarchia jest zła — przemyśl interfejsy.
I — Interface Segregation
Małe, wyspecjalizowane interfejsy zamiast jednego „grubego". Klient nie powinien zależeć od metod, których nie używa.
// źle — interfejs zmusza do implementacji wszystkiego
interface ProbeOps { void scan(); void export(); void archive(); }
// dobrze — rozdzielone role
interface Scannable { void scan(); }
interface Exportable { String export(); }
D — Dependency Inversion
Zależymy od abstrakcji, nie od konkretów. Klasy wysokiego poziomu nie tworzą zależności same — dostają je przez konstruktor.
// dobrze — zależność od interfejsu, wstrzyknięta
class InventoryService {
private final ProbeRepository repository; // interfejs, nie klasa konkretna
InventoryService(ProbeRepository repository) {
this.repository = repository;
}
}
Praktyki czystego kodu
| Zasada | Co znaczy |
|---|---|
| Wyrażające nazwy | runScan zamiast proc, bez magicznych liczb |
| Krótkie metody | jedna metoda = jedno zadanie |
| Bez duplikacji (DRY) | wspólny kod wydzielony, nie skopiowany |
| Niezmienność | record, pola final, brak setterów gdzie się da |
| Mało parametrów | więcej niż 3-4 → opakuj w obiekt |
// magiczna liczba → nazwana stała
private static final int STALE_AFTER_MINUTES = 1440;
if (probe.minutesSinceLastSeen() > STALE_AFTER_MINUTES) {
probe.markStale();
}
Wskazówka: SOLID to wskazówki, nie dogmat. Nie wprowadzaj abstrakcji „na zapas" dla pojedynczej implementacji — to YAGNI. Refaktoruj, gdy pojawia się druga implementacja albo realna potrzeba zmiany.
Pięć zasad SOLID plus dyscyplina czystego kodu dają serwisy, które zmienia się bez strachu i testuje bez gimnastyki. To inwestycja, która zwraca się przy każdym kolejnym wymaganiu.