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.
Scroll to Top