
Wprowadzenie
W poniższym artykule przedstawię drugą z zasad SOLID wraz z przykładem jej zastosowania.
OCP – Open/Closed Principle (Zasada otwarte – zamknięte)
Zasada OCP mówi nam o tym, że klasy aplikacji powinny być projektowane w taki sposób aby możliwe było dodawanie nowych funkcjonalności, bez konieczności wprowadzania modyfikacji do istniejącego kodu. Modyfikacja jest bardzo niepożądana, ponieważ zmiana w jednym miejscu aplikacji może spowodować awarię w jej całkiem innym miejscu.
Przykład zastosowania
Polimorfizm
Polimorfizm jest najczęściej używanym mechanizmem, pozwalającym nam pisać kod zgodnie z zasadą OCP.
Spójrzmy na poniższy przykład.
//przykład łamiący zasadę OCP
public class Square {
public int sideLength;
}
public class Triangle {
public int sideLengthA;
public int sideLengthB;
public int sideLengthC;
}
public class Calculator {
public int calculatePerimeter(Object shape) {
if (shape instanceof Square) {
return ((Square) shape).sideLength * 4;
} else if (shape instanceof Triangle) {
Triangle triangle = (Triangle) shape;
return triangle.sideLengthA + triangle.sideLengthB + triangle.sideLengthC;
}
return 0;
}
}
W powyższym przykładzie dodanie nowej figury np. prostokąta wiąże się z koniecznością modyfikacji klasy kalkulatora, co powoduje złamanie zasady otwarty/zamknięty (klasa nie jest otwarta na rozbudowę).
Z pomocą przychodzi nam polimorfizm – każda z klas reprezentujących figury może implementować interfejs Shape oraz metodę liczącą odwód.
//przykład z uwzględnieniem zasady OCP
public interface Shape {
int perimeter();
}
public class Square implements Shape {
public int sideLength;
@Override
public int perimeter() {
return sideLength * 4;
}
}
public class Triangle implements Shape {
public int sideLengthA;
public int sideLengthB;
public int sideLengthC;
@Override
public int perimeter() {
return sideLengthA + sideLengthB + sideLengthC;
}
}
public class Calculator {
public int calculatePerimeter(Shape shape) {
return shape.perimeter();
}
}
Dziedziczenie
Problem bardzo podobnie można rozwiązać przy użyciu dziedziczenia.
//przykład z uwzględnieniem zasady OCP
public abstract class Shape {
public abstract int perimeter();
}
public class Square extends Shape {
public int sideLength;
@Override
public int perimeter() {
return sideLength * 4;
}
}
public class Triangle extends Shape {
public int sideLengthA;
public int sideLengthB;
public int sideLengthC;
@Override
public int perimeter() {
return sideLengthA + sideLengthB + sideLengthC;
}
}
public class Calculator {
public int calculatePerimeter(Shape shape) {
return shape.perimeter();
}
}
Podsumowanie
Aby spełnić ten wymóg możemy użyć polimorfizmu lub dziedziczenia – pierwsza opcja jest zdecydowania lepsza, ponieważ wprowadza do projektu warstwę abstrakcji, która oddziela sprzężenie pomiędzy poszczególnymi klasami.
Dobrym rozwiązaniem jest również zastosowanie niektórych wzorców projektowych (m.in. metoda fabrykująca, metoda szablonowa, strategia czy też budowniczy) – dzięki nim również postępować będziemy zgodnie z OCP, a jednocześnie nasze rozwiązanie zrozumiałe będzie dla innych programistów.
- Zasada pojedynczej odpowiedzialności (Single-Responsibility Principle – SRP),
- Zasada otwarte – zamknięte (Open/Closed Principle – OCP),
- Zasada podstawiania Liskov (Liskov Substitution Principle – LSP),
- Zasada segregacji interfejsów (Interface Segregation Principle – ISP),
- Zasada odwracania zależności (Dependency Inversion Principle – DIP).