poniedziałek, 11 listopada 2019

SOLID - L jak Liskov substitution principle

Kolejną, już trzecią zasadą SOLID jest Liskov subsitution principle.
Zasada ta mówi, że używając wskaźnika klasy bazowej do potomnej, powinniśmy być w stanie używać tej referencji bez znajomości implementacji klasy potomnej.

Powyższe stwierdzenie na pierwszy rzut oka może wydać się niejasne. Najłatwiej jednak jest je zobrazować przykładem, który pokazuje kiedy ta zasada jest złamana:

    public class LiskovPrinciple
    {
        public void LiskovViolation(Report report)
        {
            if (report is EmployeeReport)
            {
                ((EmployeeReport)report).GenerateEmployeeReport();
            }
            if (report is FinancialReport)
            {
                ((FinancialReport)report).GenerateFinancialReport();
            }
        }
    }

    public class Report
    {
    }

    public class EmployeeReport : Report
    {
        public void GenerateEmployeeReport()
        {
            System.Console.WriteLine("Employee report");
        }
    }

    public class FinancialReport : Report
    {
        public void GenerateFinancialReport()
        {
            System.Console.WriteLine("Financial report");
        }
    }


W przypadku powyżej funckja LiskovViolation przyjmuje jako argument obiekt typu Report. Niestety, nawet mając informację, że jest to obiekt typu Report musi sprawdzić na co dokładnie wskazuje referencja. Jeżeli w kodzie widzimy coś podobnego możemy wnioskować, że jest to jeden z symptomów złamania zasady Liskov. Często widzi się w kodzie iterowanie po kolekcji obiektów i sprawdzanie czy dany obiekt jest typu X - to także symptom łamania tej zasady.


Innym przykładem złamania tej zasady jest brak implementacji w klasie dziedziczącej. Przykładowo dla metody jest rzucany wyjątek, gdyż w naszym przypadku ta metoda może nie mieć sensu istnienia. Zobaczmy na przykład z naszego rodzimego podwórka:

        public void SecondLiskovPrincipleViolation()
        {
            ICollection<int> collection = new int[10];
            collection.Add(10);
        }

Tworzymy tablicę typu int oraz referencję typu ICollection<int>. Wszystko do tego miejsca jest jak najbardziej poprawne. W kolejnej linijce próbujemy dodać do kolekcji nowy element, bum:


Z punktu programistycznego jest to prawidłowe zachowanie - tablica ma stałą długość. Jeżeli chcemy dodać nowy element musimy stworzyć nową o większej długości i przepisać wartości ze starej tablicy do nowej. Do takich operacji zostały stworzone inne kolekcje danych jak np. lista. W klasie Array metoda Add została zaimplementowana w następujący sposób:

        int IList.Add(Object value)
        {
            throw new NotSupportedException(Environment.GetResourceString("NotSupported_FixedSizeCollection"));
        }

Widząc taki kod w swoim projekcie powinniśmy się zastanowić, czy dziedziczenie czy też implementacja interfejsu X ma rzeczywiście sens. Można rzecz że zasada Liskov uczy nas prawidłowego korzystania z polimorfizmu oraz tworzenia łatwiejszego w zarządzaniu kodu.

Brak komentarzy:

Prześlij komentarz