Teoria
Zasada open/closed mówi o tym, że elementy systemu powinny być otwarte na rozszerzanie, ale zamknięte na modyfikacje. Oznacza to, że klasy powinny mieć możliwość rozbudowy, ale bez konieczności ingerencji w istniejący już kod.
Przykład
Rozważmy program, która wylicza i wypisuje pole powierzchni poszczególnych figur geometrycznych (w języku JAVA).
public class Main {
public static void main(String[] args) {
List<Object> figures = new ArrayList<>();
Square square = new Square(4);
figures.add(square);
Rectangle rectangle = new Rectangle(3,5);
figures.add(rectangle);
Triangle triangle = new Triangle(3,3);
figures.add(triangle);
AreaCalculator areaCalculator = new AreaCalculator(figures);
areaCalculator.calculateArea();
}
}
public class AreaCalculator {
List<Object> figures = new ArrayList<>();
public AreaCalculator(List<Object> figures) {
this.figures = figures;
}
public void calculateArea(){
for (Object figure : figures){
if (figure.getClass() == Square.class){
System.out.println(((Square) figure).getArea());
} else if ( figure.getClass() == Rectangle.class) {
System.out.println(((Rectangle) figure).getArea());
} else if ( figure.getClass() == Triangle.class) {
System.out.println(((Triangle) figure).getArea());
}
}
}
}
public class Square {
private double side;
public Square(double side) {
this.side = side;
}
public double getArea(){
return side*side;
}
}
public class Rectangle {
private double sideA;
private double sideB;
public Rectangle(double sideA, double sideB) {
this.sideA = sideA;
this.sideB = sideB;
}
public double getArea(){
return sideA * sideB;
}
}
public class Triangle {
private double base;
private double height;
public Triangle(double base, double height) {
this.base = base;
this.height = height;
}
public double getArea(){
return (base*height)/2;
}
}Podane rozwiązanie działa i daje poprawne wyniki. Zastanówmy się jednak, co by się stało, jeśli chcielibyśmy dodać nową figurę geometryczną? Zaczynamy od dodania nowej klasy, następnie należy pamiętać o dodaniu argumentu do instrukcji if w AreaCalculator, co już kłóci się z zasadą open/close, ponieważ modyfikujemy istniejący kod. Taka konstrukcja jest też trudniejsza w debuggowaniu i mniej czytelna. Spróbujmy zatem pozbyć się bolączek naszego kodu przez zastosowanie interfejsu Figure, który to będzie implementowany przez poszczególne figury.
public class Main {
public static void main(String[] args) {
List<Object> figures = new ArrayList<>();
Square square = new Square(4);
figures.add(square);
Rectangle rectangle = new Rectangle(3,5);
figures.add(rectangle);
Triangle triangle = new Triangle(3,3);
figures.add(triangle);
Circle circle = new Circle(5);
figures.add(circle);
AreaCalculator areaCalculator = new AreaCalculator(figures);
areaCalculator.calculateArea();
}
}
public class AreaCalculator {
List<Figure> figures = new ArrayList<>();
public AreaCalculator(List<Figure> figures) {
this.figures = figures;
}
public void calculateArea(){
for (Figure figure : figures){
System.out.println(figure.getArea());
}
}
}
public interface Figure {
double getArea();
}
public class Square implements Figure {
public double side;
public Square(double side) {
this.side = side;
}
@Override
public double getArea(){
return side*side;
}
}
public class Rectangle implements Figure {
public double sideA;
public double sideB;
public Rectangle(double sideA, double sideB) {
this.sideA = sideA;
this.sideB = sideB;
}
@Override
public double getArea(){
return sideA * sideB;
}
}
public class Triangle implements Figure {
public double base;
public double height;
public Triangle(double base, double height) {
this.base = base;
this.height = height;
}
@Override
public double getArea(){
return (base*height)/2;
}
}
public class Circle implements Figure{
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double getArea() {
return Math.PI * radius * radius;
}
}Po wprowadzonych zmianach, nasz program stał się łatwy w rozbudowie, a co ważne nie zachodzi już konieczność modyfikacji istniejącego kodu Zastosowanie interfejsu zmusza nas, aby klasy, które go implementują przestrzegały pewnych zasad. Chroni nas to przed błędami i ułatwia implementowanie kolejnych klas, tutaj dodatkowych figur geometrycznych. Jak widzimy czytelność również znacznie się poprawiła i program jest zdecydowanie łatwiejszy w zrozumieniu.