Mnemonik SOLID - L jak Liskov Substitution Principle
Liskov Substitution Principle to prawdopodobnie jedna z najbardziej pokręconych zasad mnemonika SOLID. Po raz pierwszy została sformułowana przez Barb
Po raz pierwszy została sformułowana przez Barbarę Liskov w 1987 roku (za Wikipedią), stąd też jej nazwa. W polskim tłumaczeniu nazywamy ją zasadą podstawienia Liskov. Reguła została spopularyzowana przez Roberta C. Martina w artykule Principles of Object Oriented Design oraz książce Agile Software Development: Principles, Patterns, and Practices.
Zasadnicza treść LSP brzmi następująco:
„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 tychobiektów”.
Podejrzewam, że dla większości osób może być ona początkowo zagmatwana, dlatego postaram się przybliżyć, o co chodziło autorce, a później także mistrzowi Martinowi.
Liskov Substitution Principle
Zasadę LSP można spróbować przedstawić prościej. Klasa dziedzicząca powinna tylko rozszerzać możliwości klasy bazowej i w pewnym sensie nie zmieniać tego, co ona robiła już wcześniej. Mówiąc jeszcze inaczej — jeśli będziemy tworzyć egzemplarz klasy potomnej, to niezależnie od tego, co znajdzie się we wskaźniku na zmienną, wywoływanie metody, którą pierwotnie zdefiniowano w klasie bazowej, powinno dać te same rezultaty. LSP można też rozpisać za pomocą krótkiego, abstrakcyjnego przykładu:
- Mamy klasę A z metodą MyMethod, która zwraca wartość Z.
- Tworzymy klasę B, która dziedziczy z A.
- Niezależnie od sposobu utworzenia klasy A oraz B:
- A a = new A()
- A b1 = new B()
- B b2 = new B()
Musi zajść równość: a.MyMethod() == b1.MyMethod() == b2.MyMethod()
W innym przypadku dochodzi do pogwałcenia zasady Liskov Substition Principle.
Antyprzykład
Napisałem wyżej, o co chodzi w zasadzie Liskov Substition Principle. Dałem też krótki, abstrakcyjny przykład, który pokazywał, w jaki sposób można dopasować się do tej reguły. Z kolei teraz chcę posłużyć się standardowym dla LSP antyprzykładem, który krąży po sieci. Dla mnie bazą będzie listing przedstawiony na stronie OOdesign.com:
class Rectangle
{
protected int _width;
protected int _height;
public void SetWidth(int width)
{
this._width = width;
}
public void SetHeight(int height)
{
this._height = height;
}
public int GetWidth()
{
return this._width;
}
public int GetHeight()
{
return this._height;
}
public int GetArea()
{
return this._width * this._height;
}
}
class Square: Rectangle
{
public void SetWidth(int width)
{
this._width = width;
this._height = width;
}
public void SetHeight(int height)
{
this._width = height;
this._height = height;
}
}
Oraz kod klasy Program:
public class Program
{
public static void Main(string[] args)
{
// Wskaźnik na Rectangle i tworzymy obiekt klasy Rectangle
Rectangle r = new Rectangle();
r.SetWidth(5);
r.SetHeight(10);
Console.WriteLine(r.GetArea());
// Wskaźnik na Rectangle, ale tworzymy obiekt klasy Square
Rectangle rs = new Square();
rs.SetWidth(5);
rs.SetHeight(10);
Console.WriteLine(rs.GetArea());
// Wskaźnik na Sqaure i tworzymy obiekt klasy Square
Square s = new Square();
s.SetWidth(5);
s.SetHeight(10);
Console.WriteLine(s.GetArea());
}
}
Niby nie ma w tym kodzie nic skomplikowanego i jest bardzo krótki, jednak w tych kilkudziesięciu liniach łamana jest zasada LSP, ponieważ osiągany rezultat nie zawsze jest taki sam!
Powyższy kod będzie również wyrzucać ostrzeżenia o niezamierzonej chęci przykrycia metod SetWidthoraz SetHeight. Oczywiście możemy je naprawić, dodając w klasie bazowej słowo kluczowe virtual, natomiast w klasie dziedziczącej — override. Spowoduje to, że przykłady 2. i 3. będą zwracały tę samą wartość. Reguła LSP będzie wciąż jednak pogwałcona, ponieważ przykład A będzie zwracał inną wartość dla tej samej metody. Podsumujmy: rozszerzając, nie modyfikujmy sposobu działania istniejącego kodu.
Jerzy Piechowiak
Altcontroldelete.pl
Szukasz książki lub kursu do C#? Kliknij TUTAJ!
Zobacz nasze propozycje
-
- Druk
(29,94 zł najniższa cena z 30 dni)
29.94 zł
49.90 zł (-40%) -
- Druk
- PDF + ePub + Mobi
(26,94 zł najniższa cena z 30 dni)
35.92 zł
44.90 zł (-20%) -
- Druk
- PDF + ePub + Mobi
- Audiobook MP3
(29,94 zł najniższa cena z 30 dni)
29.94 zł
49.90 zł (-40%) -
- Druk
- PDF + ePub + Mobi
(35,40 zł najniższa cena z 30 dni)
35.40 zł
59.00 zł (-40%) -
- Druk
(53,40 zł najniższa cena z 30 dni)
53.40 zł
89.00 zł (-40%) -
- Druk
- PDF + ePub + Mobi
(35,40 zł najniższa cena z 30 dni)
35.40 zł
59.00 zł (-40%) -
- Druk
- PDF + ePub + Mobi
(47,40 zł najniższa cena z 30 dni)
47.40 zł
79.00 zł (-40%) -
- Druk
- PDF + ePub + Mobi
(47,40 zł najniższa cena z 30 dni)
47.40 zł
79.00 zł (-40%) -
- Druk
- PDF + ePub + Mobi
(35,40 zł najniższa cena z 30 dni)
29.49 zł
59.00 zł (-50%) -
- Druk
- PDF + ePub + Mobi
(47,40 zł najniższa cena z 30 dni)
47.40 zł
79.00 zł (-40%) -
- Druk
- PDF + ePub + Mobi
(47,40 zł najniższa cena z 30 dni)
47.40 zł
79.00 zł (-40%) -
- Druk
- PDF + ePub + Mobi
(23,94 zł najniższa cena z 30 dni)
23.94 zł
39.90 zł (-40%) -
- Druk
(107,40 zł najniższa cena z 30 dni)
107.40 zł
179.00 zł (-40%) -
- Druk
- PDF + ePub + Mobi
(53,40 zł najniższa cena z 30 dni)
53.40 zł
89.00 zł (-40%) -
- Druk
- PDF + ePub + Mobi
(35,40 zł najniższa cena z 30 dni)
35.40 zł
59.00 zł (-40%) -
- Druk
(77,40 zł najniższa cena z 30 dni)
77.40 zł
129.00 zł (-40%) -
- Druk
- PDF + ePub + Mobi
(23,94 zł najniższa cena z 30 dni)
23.94 zł
39.90 zł (-40%) -
- Druk
- PDF + ePub + Mobi
(39,50 zł najniższa cena z 30 dni)
47.40 zł
79.00 zł (-40%) -
- Druk
- PDF + ePub + Mobi
- Audiobook MP3
(29,40 zł najniższa cena z 30 dni)
29.40 zł
49.00 zł (-40%) -
- Druk
- PDF + ePub + Mobi
(41,40 zł najniższa cena z 30 dni)
41.40 zł
69.00 zł (-40%)