Zasady S.O.L.I.D – zasada podstawienia Liskov

Na początek podam czystą definicje z wiki:

“Funkcje które używają wskaźników lub referencji do klas bazowych, muszą być w stanie używać również obiektów klas dziedziczących po klasach bazowych, bez dokładnej znajomości tych obiektów.”

Początkowo za wiele ta tajemnicza definicja nie mówiła mi. Innymi słowy, klasa dziedzicząca powinna  rozszerzać możliwości klasy bazowej a nie całkowicie zmieniać jej funkcjonalność. Sposób korzystania z klasy potomnej powinien być analogiczny do wywoływania klasy bazowej. Przyjrzyjmy się klasycznemu przykładowi łamania zasady Liskov (źródło: http://www.oodesign.com):

class Rectangle
{
    protected int m_Width;    
    protected int m_Height;
    
    public void SetWidth(int width)
    {    
        m_Width = width;    
    }
    public void SetHeight(int height)
    {    
        m_Height = height;    
    }    
    public int GetWidth()
    {        
        return m_Width;    
    }
    public int GetHeight()
    {        
        return m_Height;
    }    
    public int GetArea()
    {    
        return m_Width * m_Height;    
    }    
}
class Square: Rectangle 
{    
    public void SetWidth(int width)
    {    
        m_Width = width;        
        m_Height = width;    
    }
    public void SetHeight(int height)
    {    
        m_Width = height;        
        m_Height = height;    
    }
}

Następnie obliczmy pole:

Rectangle r = new Square();
r.SetWidth(5);    
r.SetHeight(10);        
int area = r.GetArea();

Użytkownik spodziewa się wyniku 50 a otrzymuje 100. Jest to przykład łamania zasady Liskov – sekwencja kodu przynosi różne efekty w zależności od tego czy posiadamy referencję na klasę bazową czy potomną. Klasa potomna nie powinna zmieniać zachowania klasy.

5 thoughts on “Zasady S.O.L.I.D – zasada podstawienia Liskov”

  1. Jeżeli to jest kod C#, to:

    Użytkownik spodziewa się wyniku 50…. i otrzymuje 50. W przykładzie mamy do czynienia z przeciążeniem metod (overloading), a nie nie ich przesłanianiem (overriding) – w tym przypadku wynik byłby 100.
    Jeżeli to jest C# to brakuje słów kluczowych “virtual” w klasie bazowej i “override” w klasie pochodnej.

  2. @paweln66
    W przykładzie jest przesłanianie (overriding) i słowo kluczowe “virtual” stosujemy właśnie przy przesłonionych metodach, a nie jak można by wywnioskować z Twojego komentarza – przeciążonych.

  3. Wziąłem i sprawdziłem, wstawiłem kod do VS i wykonałem.
    No i paweln66 ma rację. Wynik tego kawałka kodu jest równy 50 dlatego, że mamy tu do czynienia z polimorfizmem statycznym, a więc przesłanianiem metod i już na etapie kompilacji wiadomo, że mają być wykonane metody klasy Rectangle.
    Gdybyśmy napisali deklarację zmiennej r w ten sposób:
    var r = new Square();
    lub ten:
    Square r = new Square();
    to wynik byłby rzeczywiście 100, aczkolwiek ustawianie wysokości i szerokości kwadrata raczej mija się z celem.

Leave a Reply

Your email address will not be published.