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