SOLID – Zasada Segregacji Interfejsów

W tym wpisie przedstawię Ci, czym jest zasada segregacji interfejsów. Jest to 4 zasada z 5 mnemonika SOLID.

SOLID – Zasada Segregacji Interfejsów

I literka I na naszej liście. Co oznacza, że w tym wpisie przedstawię Ci zasadę segregacji interfejsów. No właśnie, czym jest zasada segregacji interfejsów?

SOLID

SZasada pojedynczej odpowiedzialności (Single Responsibility Principle – SRP)
OZasada otwarte – zamknięte – (Open-Closed Principle – OCP)
LZasada podstawiania Liskov  – (Liskov Substitution Principle – LSP)
I  – Zasada segregacji interfejsów – (Interface Segregation Principle – ISP)
DZasada odwracania zależności – (Dependency Inversion  Principle DIP)

Zasada Segregacji Interfejsów

Podstawą rozważań na temat tej zasady są interfejsy. Poprzez interfejs możemy rozumieć klasę abstrakcyjną z metodami abstrakcyjnymi lub zwykły interfejs. W niniejszym artykule będę posługiwał się terminem interfejs, mając na myśli obie te rzeczy.

Podczas implementacji interfejsie jesteśmy zmuszeni do zaimplementowania każdej jego składowej. Czy zdarzyło Ci się, że korzystając z jakiegoś interfejsu w nowo tworzonej klasie, nie wypełniłeś ciała którejś z jego metod? Jeżeli tak, to naruszyłeś zasadę segregacji interfejsu. A mówi ona:

Załóżmy, że implementujesz interfejs. Jeśli zauważysz, że się, że jakaś metoda lub propercja nie jest Ci w niej potrzebna i implementacja jest pusta/domyślna, to w takim przypadku powinieneś przemyśleć strukturę tego interfejsu. W takim przypadku błędnie zaprojektowałeś hierarchię. Powinieneś dokonać podziału tego interfejsu na kilka mniejszych.

Praktyczny przykład

Wszystko łatwiej zrozumieć na przykładzie. Posłużę się lekko zmodyfikowanym kodem z poprzedniego wpisu o mnemoniku SOLID.

public interface IVehicle
{
    double Combustion { get; set; }
    string Name { get; set; }
    int WheelsCount { get; set; }

    void About();
}

public class Car : IVehicle
{
    public string Type { get; set; }
    public double Combustion { get; set; }
    public string Name { get; set; }
    public int WheelsCount { get; set; }

    public void About()
    {
        Console.WriteLine($"I'm {Type} - {Name} - with {Combustion}L combustion and {WheelsCount} wheels count!");
    }
}

public class ElectricCar : IVehicle
{
    public int BatteriesCount { get; set; }

    public void About()
    {
        Console.WriteLine($"I'm  {Name} - with {WheelsCount} wheels count and {BatteriesCount} batteries count!");
    }

    public double Combustion { get; set; }
    public string Name { get; set; }
    public int WheelsCount { get; set; }
}


internal class Program
{
    private static void Main()
    {
        var electricCar = new ElectricCar();
        var car = new Car();

        car.Name = "Tesla";
        car.WheelsCount = 4;
        car.Type = "Muscle Car";
        car.Combustion = 10.5f;

        electricCar.Name = "Tesla";
        electricCar.WheelsCount = 4;
        electricCar.BatteriesCount = 10;

        electricCar.About();
        car.About();

        Console.WriteLine();
        Console.ReadKey();
    }
}

Wynik działania.

Wszystko działa poprawnie. Zwróć, proszę uwagę na kod programu, a dokładnie na klasę ElectricCar. Właściwość Combustion musi zostać zaimplementowana mimo że nie będzie używana. Rozwiążmy ten problem.

Sposobów na poprawienie tego problemu jest wiele. Jednym z nich jest utworzenie nowego interfejsu dla pojazdów elektrycznych dziedziczący po IVehicle⁣, ale przedtem należy zmodyfikować ten interfejs.

public interface IVehicle
{
    string Name { get; set; }
    int WheelsCount { get; set; }
    void About();
}

public interface IElectricVehicle : IVehicle
{
    int BatteriesCount { get; set; }
}

public class Car : IVehicle
{
    public string Type { get; set; }
    public double Combustion { get; set; }
    public string Name { get; set; }
    public int WheelsCount { get; set; }

    public void About()
    {
        Console.WriteLine($"I'm {Type} - {Name} - with {Combustion}L combustion and {WheelsCount} wheels count!");
    }
}

public class ElectricCar : IElectricVehicle
{
    public string Name { get; set; }
    public int WheelsCount { get; set; }
    public int BatteriesCount { get; set; }

    public void About()
    {
        Console.WriteLine($"I'm  {Name} - with {WheelsCount} wheels count and {BatteriesCount} batteries count!");
    }

}

internal class Program
{
    private static void Main()
    {
        var electricCar = new ElectricCar();
        var car = new Car();

        car.Name = "Tesla";
        car.WheelsCount = 4;
        car.Type = "Muscle Car";
        car.Combustion = 10.5f;

        electricCar.Name = "Tesla";
        electricCar.WheelsCount = 4;
        electricCar.BatteriesCount = 10;

        electricCar.About();
        car.About();

        Console.WriteLine();
        Console.ReadKey();
    }
}

Przykład, choć naiwny przedstawił główne założenie, jakie ze sobą niesie zasada segregacji interfejsu.