SOLID – Zasada otwarte - zamknięte
Zasada otwarte - zamknięte (Open Closes Principle – OCP) i jej konkretny opis. SOLID bez tajemnic.

Zasada otwarte — zamknięte jest drugą zasadą mnemoniku SOLID. Co dokładnie ona oznacza?
S – Zasada pojedynczej odpowiedzialności (Single Responsibility Principle – SRP)
O – Zasada otwarte – zamknięte – (Open-Closed Principle – OCP)
L – Zasada podstawiania Liskov – (Liskov Substitution Principle – LSP)
I – Zasada segregacji interfejsu – (Interface Segregation Principle – ISP)
D – Zasada odwracania zależności – (Dependency Inversion Principle DIP)
Zasada otwarte – zamknięte
W programowaniu pewnikiem są zmiany. Są one nieodłącznym aspektem rozwijania projektu. Nie jesteśmy w stanie w 100% zabezpieczyć naszego kodu przed nimi, aczkolwiek istnieje reguła, która może nam w tym pomóc. Tą zasadą jest właśnie otwarte – zamknięte. Mówi ona tyle, że nasz kod powinien być tak skonstruowany w taki sposób, aby był zamknięty na edycję, ale był otwarty na rozszerzenia.
Na pierwszy rzut oka wydaję się, że jest tu jakaś nieścisłość. W jaki sposób mamy rozwijać i zmieniać zachowanie już istniejących klas bez ich aktualizacji?
Jest to możliwe, a odpowiedzią na to pytanie jest programowanie obiektowe a dokładniej abstrakcja. Spójrz poniżej:
public static class AreaCalculator
{
public static double CalculateRectanglesAreas (Rectangle[] rectangles)
{
double area = 0;
foreach (var rectangle in rectangles)
area += rectangle.Height * rectangle.Width;
return area;
}
public static double CalculateTrianglesAreas (Triangle[] triangles)
{
double area = 0;
foreach (var triangle in triangles)
area += (triangle.Base * triangle.Height) / 2;
return area;
}
}
Mamy tu dwie metody przeznaczone do obliczania sumy pól figur przekazanych jako parametr. Oddzielna istnieje dla trójkątów i oddzielna dla prostokątów. Kiedy zechcemy dodać kolejną figurę, będziemy musieli zmodyfikować klasę i dodać kolejną metodę. Łamiemy przy okazji inną ważną zasadę wytwarzania oprogramowania o nazwie DRY (Don’t Repeat Yourself – Nie powtarzaj się), ponieważ metody te robią w zasadzie to samo, a są rozbite tylko ze względu na typ parametru.
Czysty kod. Podręcznik dobrego programisty
Czysty kod. Podręcznik dobrego programisty
Refaktoryzacja
Czy nie byłoby wspaniale mieć tylko jedną funkcję do obliczania sumy pól danej figury i być zamkniętym na dodawanie kolejnej metody po dodaniu nowej figury do systemu? Jasne, że tak.
Utwórzmy zatem uniwersalną metodę, która jako swój parametr przyjmie abstrakcyjny byt o nazwie Shape
. Wtedy jedną funkcją moglibyśmy załatwić problem dodania obliczania pól dla nowych figur, które pojawiłyby się w przyszłości. Jak tego dokonać? Wykorzystajmy dobrodziejstwo obiektowości.
Zacznijmy od utworzenia abstrakcyjnej klasy bazowej o nazwie Shape
.
public abstract class Shape
{
public abstract double CalculateArea ();
}
Warto zaznaczyć, że równoważnym rozwiązaniem byłoby utworzenie interfejsu.
Następnie utwórzmy klasę Rectangle
.
public class Rectangle : Shape
{
public double Height { get; set; }
public double Width { get; set; }
public override double CalculateArea ()
{
return Width * Height;
}
}
Dodałem klasę AreaCalculator
, a do jej wnętrza przeniosłem zmodyfikowaną metodę CalculateArea()
.
public static class AreaCalculator
{
public double CalculateArea (Shape[] shapes)
{
double area = 0;
foreach (var shape in shapes)
area += shape.CalculateArea ();
return area;
}
}
Teraz gdy przekażemy do tej metody jako parametr tablicę obiektów Rectangle
czy Triangle
metoda będzie zwracać poprawne wyniki w zależności od rodzaju przekazanej figury. A to wszystko dzięki polimorfizmowi. Teraz gdy przyjdzie nam dodać nową figurę, nie będziemy musieli dodawać nowej metody, która będzie przyjmować ten nowy kształt. Wystarczy, że przy tworzeniu nowej figury użyjemy dziedziczenia i przysłonimy odpowiednią metodę.
Zamknęliśmy naszą klasę na modyfikacje, a dodatkowo dodaliśmy możliwość obliczania pól dla nowych figur bez edycji już istniejącego kodu.