Spis treści
Liskov substitution principle – Zasada podstawienia Liskov
Oprogramowanie powinno dobrze działać, gdy w miejsce klasy bazowej podstawimy jej którąkolwiek klasę potomną.
Jak wykorzystywać w praktyce LSP?
Należy dobrze zastanowić się przed skorzystaniem z dziedziczenia. Zazwyczaj lepszą opcją jest użycie kompozycji. Warto jest również zrobić proste eksperymenty: próbować podstawić w klasach korzystających z naszego drzewa klas, wszystkie klasy potomne oraz klasę bazową i zobaczyć, czy nadal program zachowuje się odpowiednio, czy może jednak oczekuje tylko jednego konkretnego rodzaju klasy.
Przykład
Rozważmy takie zadanie:
Mamy napisać aplikację, który wyświetla obraz z kamery video. Do naszej aplikacji możemy podłączyć wiele kamer. Nie wszystkie z nich potrafimy obsłużyć. Na szczęście wiemy to już na początku. Tworzymy klasę bazową Camera oraz klasy dziedziczące po kamerze, w tym również klasę dla kamery nieobsługiwanej.
class Program
{
static void Main(string[] args)
{
var cameras = new List<Camera>();
var cameraNC1 = new CameraNC1("Front camera");
cameras.Add(cameraNC1);
var cameraShower = new CameraShower(cameraNC1);
cameraShower.Show();
var cameraNC2 = new CameraNC2("Back camera");
cameras.Add(cameraNC2);
cameraShower = new CameraShower(cameraNC2);
cameraShower.Show();
var unsupportedCamera = new UnsupportedCamera(null);
cameras.Add(unsupportedCamera);
cameraShower = new CameraShower(unsupportedCamera);
cameraShower.Show();
}
}
internal class CameraShower
{
private Camera camera;
public CameraShower(Camera camera)
{
this.camera = camera;
}
internal void Show()
{
if (!(camera is UnsupportedCamera))
Console.WriteLine($"Camera name: {camera.GetName()}");
}
}
internal class CameraNC2 : Camera
{
public CameraNC2(string name) : base(name)
{
}
}
internal class CameraNC1 : Camera
{
public CameraNC1(string name) : base(name)
{
}
}
internal class UnsupportedCamera : Camera
{
public UnsupportedCamera(string name) : base(name)
{
}
internal override string GetName()
{
throw new Exception("This camera does not have name. It is not supported.");
}
}
internal abstract class Camera
{
protected readonly string name;
public Camera(string name)
{
this.name = name;
}
internal virtual string GetName()
{
return name;
}
}
Wszystko pięknie działa. Ale musieliśmy kamerę niewspieraną specjalnie obsłużyć. Jeżeli byśmy tego nie zrobili, dostalibyśmy wyjątek. Możemy trochę inaczej zaimplementować metodę GetName w klasie dla kamery niewspieranej:
class Program
{
static void Main(string[] args)
{
var cameras = new List<Camera>();
var cameraNC1 = new CameraNC1("Front camera");
cameras.Add(cameraNC1);
var cameraShower = new CameraShower(cameraNC1);
cameraShower.Show();
var cameraNC2 = new CameraNC2("Back camera");
cameras.Add(cameraNC2);
cameraShower = new CameraShower(cameraNC2);
cameraShower.Show();
var unsupportedCamera = new UnsupportedCamera();
cameras.Add(unsupportedCamera);
cameraShower = new CameraShower(unsupportedCamera);
cameraShower.Show();
}
}
internal class CameraShower
{
private Camera camera;
public CameraShower(Camera camera)
{
this.camera = camera;
}
internal void Show()
{
Console.WriteLine($"Camera name: {camera.GetName()}");
}
}
internal class CameraNC2 : Camera
{
public CameraNC2(string name) : base(name)
{
}
}
internal class CameraNC1 : Camera
{
public CameraNC1(string name) : base(name)
{
}
}
internal class UnsupportedCamera : Camera
{
public UnsupportedCamera() : base("Unknown - this camera is not supported")
{
}
}
internal abstract class Camera
{
protected readonly string name;
public Camera(string name)
{
this.name = name;
}
internal virtual string GetName()
{
return name;
}
}
Wszystkie posty związane z mini projektem: Poznaj zasady SOLID i OOP:
- Początek mini projektu: Poznaj zasady SOLID i OOP
- SOLID
- S jak Single responsibility principle, czyli zasada jednej odpowiedzialności
- O jak Open-closed principle, czyli zasada otwarte/zamknięte
- L jak Liskov Substitution Principle, czyli zasada podstawień Barbary Liskov
- I jak Interface segregation principle, czyli Zasada segregacji interfejsów
- D jak Dependency Inversion Principle, czyli Zasada odwrócenia zależności
- CS jak Ćwiczenia Single responsibility principle, czyli zasada jednej odpowiedzialności
- CO jak Ćwiczenia Open/closed principle, czyli Zasada otwarte-zamknięte
- CL jak Ćwiczenia Liskov Substitution Principle, czyli zasada podstawień Barbary Liskov
- CI jak Ćwiczenia Interface segregation principle, czyli Zasady segregacji interfejsów
- CI jak Ćwiczenia Dependency Inversion Principle, czyli Zasada odwrócenia zależności
- Ćwiczenia z SOLID
- Podsumowanie połowy projektu: Poznaj zasady SOLID i OOP
- Ćwiczenia z SOLID — Kata
- OOP — Object Oriented Programming, czyli programowanie obiektowe
- OOP — Myślenie obiektowe
- OOP — Object Oriented Programming, czyli programowanie obiektowe — Modelowanie dziedziny
- KISS — Keep it simple, stupid, czyli Bez udziwnień zapisu, idioto (BUZI)
- Lod — Law of Demeter, czyli Prawo Demeter
- DRY — Don’t repeat yourself, czyli Nie powtarzaj się
- SLAP — Single Level of Abstraction Principle, czyli Pojedynczy poziom abstrakcji
- Composition Over Inheritance, czyli Kompozycja ponad dziedziczeniem
- Encapsulate what changes, czyli Ukrywaj zmieniające się rzeczy
- Podsumowanie projektu: Poznaj zasady SOLID i OOP
- Podsumowanie zasad SOLID i OOP
- Mini kurs: Poznaj zasady SOLID i OOP – Zapisz się!
- Praca cząstkowa w Metodzie Kanban
Źródła
Obraz główny
Materiały
- Czysta architektura — Robert C. Martin
- https://medium.com/hackernoon/the-liskov-substitution-principle-and-why-you-might-want-to-enforce-it-6f5bbb05c06d
- https://code.tutsplus.com/tutorials/solid-part-3-liskov-substitution-interface-segregation-principles–net-36710