Spis treści
Open/closed principle – Zasada otwarte-zamknięte
Wszystkie klasy powinny być otwarte na rozszerzenia, ale zamknięte na modyfikacje.
Jak wykorzystywać w praktyce OCP?
Warto jest stosować interfejsy oraz operować na abstrakcji, a nie uzależniać się od szczegółów implementacyjnych.
Trudności związane z OCP
Ciężko jest czasami zdecydować, co jest szczegółem implementacyjnym oraz w którą stronę będzie rozszerzała się nasza aplikacja.
Przykład
Ćwiczenia tej metody wykonałem na bardzo prostym przykładzie. Mamy kalkulator, który ma za zadanie obliczyć powierzchnię figur. W pierwszej wersji używa on switcha, do rozpoznania, jaka jest to klasa.
class AreaCalculator { private readonly List<object> figures; public AreaCalculator(List<object> figures) { this.figures = figures; } public double Calculate() { double area = 0; foreach (var figure in figures) { switch (figure) { case Square square: area += square.GetWidth() * square.GetWidth(); break; case Rectangle rectangle: area += rectangle.GetWidth() * rectangle.Getheight(); break; case Circle circle: area += Math.PI * Math.Pow(circle.GetRadius(), 2); break; default: area += area; break; } } return area; } } class Square { private readonly double width; public Square(double width) { this.width = width; } internal double GetWidth() { return width; } } class Rectangle { private readonly double width; private readonly double height; public Rectangle(double width, double height) { this.width = width; this.height = height; } internal double GetWidth() { return width; } internal double Getheight() { return height; } } class Circle { private readonly double radius; public Circle(double radius) { this.radius = radius; } internal double GetRadius() { return radius; } } class Program { static void Main(string[] args) { List<object> figures = new List<object>() { new Square(5), }; AreaCalculator areaCalculator = new AreaCalculator(figures); Debug.Assert(areaCalculator.Calculate() == 25); List<object> figures2 = new List<object>() { new Square(5), new Rectangle(5,3), }; Debug.Assert(new AreaCalculator(figures2).Calculate() == (25 + 15)); List<object> figures3 = new List<object>() { new Square(5), new Rectangle(5,3), new Circle(5), }; Debug.Assert(new AreaCalculator(figures3).Calculate() == (25 + 15 + Math.PI * 25)); } }
W drugim przypadku klasy figur implementują interfejs, który ma metodę do liczenia pola powierzchni klasy. Każda klasa sama implementuję liczenie swojego pola powierzchni. Kalkulator jedynie sumuje pola powierzchni.
class AreaCalculator { private readonly IEnumerable<IFigureAreaCalculator> figures; public AreaCalculator(IEnumerable<IFigureAreaCalculator> figures) { this.figures = figures; } public double Calculate() { return figures.Sum(x => x.GetArea()); } } interface IFigureAreaCalculator { double GetArea(); } class Square : IFigureAreaCalculator { private readonly double width; public Square(double width) { this.width = width; } public double GetArea() { return width * width; } internal double GetWidth() { return width; } } class Rectangle : IFigureAreaCalculator { private readonly double width; private readonly double height; public Rectangle(double width, double height) { this.width = width; this.height = height; } internal double GetWidth() { return width; } internal double Getheight() { return height; } public double GetArea() { return width * height; } } class Circle : IFigureAreaCalculator { private readonly double radius; public Circle(double radius) { this.radius = radius; } public double GetArea() { return Math.PI * Math.Pow(radius, 2); } internal double GetRadius() { return radius; } } class Program { static void Main(string[] args) { var figures = new List<IFigureAreaCalculator>() { new Square(5), }; AreaCalculator areaCalculator = new AreaCalculator(figures); Debug.Assert(areaCalculator.Calculate() == 25); var figures2 = new List<IFigureAreaCalculator>() { new Square(5), new Rectangle(5,3), }; Debug.Assert(new AreaCalculator(figures2).Calculate() == (25 + 15)); var figures3 = new List<IFigureAreaCalculator>() { new Square(5), new Rectangle(5,3), new Circle(5), }; Debug.Assert(new AreaCalculator(figures3).Calculate() == (25 + 15 + Math.PI * 25)); } }
Dodanie nowej figury nie będzie wymagało zmiany w klasie AreaCalculator.
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://hackernoon.com/why-the-open-closed-principle-is-the-one-you-need-to-know-but-dont-176f7e4416d
- https://code.tutsplus.com/tutorials/solid-part-2-the-openclosed-principle–net-36600