SOLID: Interface Segregation Principle (Zasada segregowania interfejsów)

Wprowadzenie

W poprzednim artykule pisałam o zasadzie podstawiania Liskov. Kolejną z zasad SOLID jest segregacja interfejsów opracowana przez Roberta C. Martina, który pracował wtedy w firmie Xerox. Podczas opracowywania nowego systemu, Martin zauważył, że drobne zmiany w projekcie powodowały konieczność modyfikacji na dużą skalę miejsc w kodzie, które wychodziły poza zakres zmian – spowodowane to było głównie przez duży, “załadowany” interfejs, używamy we wszystkich modułach systemu. Zobaczmy zatem jakie rozwiązanie tego problemu wymyślił Martin.

ISP – Interface Segregation Principle (Zasada segregowania interfejsów)

"Klienci nie powinni być zmuszani do polegania na interfejsach, których nie używają."
"Wiele dedykowanych interfejsów jest lepsze niż jeden ogólny."
"Interfejsy powinny być konkretne i jak najmniejsze."

Jak widać zasada ma wiele definicji, każda z nich jest jasna i zrozumiała – nie należy zmuszać klas do implementacji metod, których nie używają. Głównym celem stosowania tej zasady, podobnie jak w przypadku zasady pojedynczej odpowiedzialności, jest ograniczenie zakresu przyszłych zmian w aplikacji.

Przykład zastosowania

Rozważmy aplikację, która odpowiedzialna jest za dostarczenie systemu drukowania dla marki drukarek wielofunkcyjnych. System musi być opracowany w taki sposób, aby możliwe było dodawanie nowych typów drukarek, bez konieczności modyfikacji w całym systemie.

//przykład łamiący zasadę ISP

public interface AllInOnePrinter {
    void printInColour();
    void printInBlack();
    void scan();
    void fax();
}

public class Colour1234Printer implements AllInOnePrinter {
    @Override
    public void printInColour() {
        //logic for printing in colour
    }

    @Override
    public void printInBlack() {
        //logic for printing in black
    }

    @Override
    public void scan() {
        //logic for scanning
    }

    @Override
    public void fax() {
        //logic for faxing
    }
}

public class Black4321Printer implements AllInOnePrinter {

    @Override
    public void printInColour() {
        throw new UnsupportedOperationException("Printing in colour is not supported.");
    }

    @Override
    public void printInBlack() {
        //logic for printing in black
    }

    @Override
    public void scan() {
        //logic for scanning
    }

    @Override
    public void fax() {
        throw new UnsupportedOperationException("Faxing is not supported.");
    }
} 

Jak widzimy drukarka Black4321Printer nie obsługuje wszystkich działań zawartych w głównym interfejsie AllInOnePrinter. Konieczne jest rzucanie wyjątku podczas próby skanowania lub druku w kolorze – właśnie w tym miejscu dochodzi do naruszenie zasady ISP.

Główny problem w powyższym przykładzie polega na tym, że używamy ogólnego “grubego” interfejsu, zamiast mniejszych i wyspecjalizowanych. Aby rozwiązać ten problem, musimy podzielić bazowy interfejs na kilka mniejszych i bardziej konkretnych.

//przykład z uwzględnieniem zasady ISP

public interface BlackPrinter {
    void printInBlack();
}

public interface ColourPrinter {
    void printInColour();
}

public interface FaxingPrinter {
    void fax();
}

public interface ScanningPrinter {
    void scan();
}

public class Black4321Printer implements BlackPrinter, ScanningPrinter {

    @Override
    public void printInBlack() {
        //logic for printing in black
    }

    @Override
    public void scan() {
        //logic for scanning
    }
}

public class Colour1234Printer implements ColourPrinter, BlackPrinter, ScanningPrinter, FaxingPrinter {
    @Override
    public void printInColour() {
        //logic for printing in colour
    }

    @Override
    public void printInBlack() {
        //logic for printing in black
    }

    @Override
    public void scan() {
        //logic for scanning
    }

    @Override
    public void fax() {
        //logic for faxing
    }
}
 

Po refaktoryzacji nasz system jest zgodny z Zasadą Segregacji Interfejsów – drukarki implementują tylko te interfejsy, których metody są przez nie obsługiwane, dlatego nie ma konieczności rzucania wyjątków.

Różnica między zasadą segregacji interfejsów a zasadą substytucji Liskov

Wielu programistów mylnie twierdzi, że obie te zasady są takie same. Główna różnica między nimi polega na tym, że LSP skupia się na powiązaniach między klasą abstrakcyjną a jej podklasami, zaś zasada ISP zajmuje się segregowaniem interfejsów i ich dzieleniem na mniejsze i wyspecjalizowane.

Podsumowanie

Zarówno zasada segregacji interfejsów, jak i zasada pojedynczej odpowiedzialności mają ten sam cel: dostarczenie małych, skoncentrowanych i wysoce spójnych komponentów aplikacji, które zapewniają jej elastyczność i czynią łatwą w utrzymaniu. Różnica między nimi polega na tym, że SRP dotyczy głównie klas, gdy ISP odnosi się tylko do interfejsów. Zasada ISP jest stosunkowa prosta i łatwa do przestrzegania. Ważne jest jednak aby stosować ją rozważnie, gdyż bardzo łatwo można doprowadzić do utworzenia zbyt dużej ilości niepotrzebnych interfejsów.

 

Leave a Comment

Your email address will not be published. Required fields are marked *