Interface Segregation Principle
Interface Segregation Principle – ISP
Zasada segregacji interfejsów (ISP) mówi, że klasy nie powinny być zmuszane do implementowania metod, których nie używają. Zamiast tworzyć jeden duży interfejs z wieloma metodami, lepiej jest podzielić go na kilka mniejszych, bardziej specyficznych interfejsów. Każdy interfejs powinien oferować funkcjonalność związaną z jednym, konkretnym zadaniem lub rolą. Dzięki temu klasy mogą implementować tylko te interfejsy, które naprawdę odpowiadają ich potrzebom, co prowadzi do bardziej przejrzystego, elastycznego i łatwego w utrzymaniu kodu.
Zalety:
- Lepsza czytelność i łatwość utrzymania kodu: Klient implementuje tylko te metody, które są mu potrzebne.
- Zmniejszona ilość zmian: Jeśli zmieniamy interfejs, który ma wiele metod, zmusza to wszystkie klasy, które go implementują, do aktualizacji. Mniejsze, wyspecjalizowane interfejsy ograniczają ten problem.
- Większa elastyczność: Klasy mogą implementować tylko te interfejsy, które są dla nich istotne, co pozwala na bardziej zróżnicowane i elastyczne projekty.
Przykład z życia codziennego:
Restauracja
Bez zastosowania zasady: Wyobraź sobie restaurację, w której każdy pracownik, niezależnie od swojej roli, musi umieć robić wszystko — gotować, serwować jedzenie, sprzątać stoliki, obsługiwać kasę, a także zarządzać magazynem. Zamiast specjalizacji, wszyscy pracownicy są zobowiązani do przestrzegania jednej ogólnej listy obowiązków. W praktyce oznacza to, że np. kucharz, który specjalizuje się w gotowaniu, musi także znać się na obsłudze kasy czy zarządzaniu magazynem, co jest nieefektywne i może prowadzić do błędów.
Z wykorzystaniem zasady: W dobrze zorganizowanej restauracji zadania są podzielone zgodnie z kompetencjami pracowników. Mamy kucharzy, którzy zajmują się tylko gotowaniem, kelnerów, którzy obsługują klientów i serwują jedzenie, sprzątaczy, którzy dbają o czystość stolików, kasjerów, którzy obsługują płatności, oraz menedżerów magazynu, którzy zajmują się zaopatrzeniem. Każda rola ma przypisane konkretne obowiązki i nie jest zmuszona do wykonywania zadań, które nie są jej specjalnością.
Przykład programu przed użyciem zasady ISP:
Załóżmy, że mamy interfejs ZwierzeDomowe
, który zawiera metody jedz()
, bawSie()
i aportuj()
. Problem pojawia się, gdy różne zwierzęta, takie jak koty i psy, są zmuszane do implementowania wszystkich tych metod, nawet jeśli nie mają sensu dla danego zwierzęcia.
interface ZwierzeDomowe {
void jedz();
void bawSie();
void aportuj();
}
class Pies implements ZwierzeDomowe {
@Override
public void jedz() {
System.out.println("Pies je.");
}
@Override
public void bawSie() {
System.out.println("Pies się bawi.");
}
@Override
public void aportuj() {
System.out.println("Pies aportuje.");
}
}
class Kot implements ZwierzeDomowe {
@Override
public void jedz() {
System.out.println("Kot je.");
}
@Override
public void bawSie() {
System.out.println("Kot się bawi.");
}
@Override
public void aportuj() {
throw new UnsupportedOperationException("Kot nie aportuje.");
}
}
W powyższym kodzie kot jest zmuszony do implementowania metody aportuj()
, co nie ma sensu i prowadzi do rzucenia wyjątku.
Poprawiony kod zgodny z zasadą ISP
Zamiast używać jednego dużego interfejsu, rozdzielamy go na mniejsze, bardziej wyspecjalizowane interfejsy.
interface Jedzace {
void jedz();
}
interface BawiaceSie {
void bawSie();
}
interface Aportujace {
void aportuj();
}
class Pies implements Jedzace, BawiaceSie, Aportujace {
@Override
public void jedz() {
System.out.println("Pies je.");
}
@Override
public void bawSie() {
System.out.println("Pies się bawi.");
}
@Override
public void aportuj() {
System.out.println("Pies aportuje.");
}
}
class Kot implements Jedzace, BawiaceSie {
@Override
public void jedz() {
System.out.println("Kot je.");
}
@Override
public void bawSie() {
System.out.println("Kot się bawi.");
}
}
Podsumowanie
- Zły przykład: Koty są zmuszane do implementowania metody
aportuj()
, która nie jest dla nich naturalna, co prowadzi do błędów. - Dobry przykład: Koty implementują tylko te metody, które mają dla nich sens, co sprawia, że kod jest bardziej zgodny z rzeczywistością i łatwiejszy do utrzymania.