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