OCP - Open-Closed Principle
Както подсказва името, този принцип гласи, че софтуерните обекти трябва да бъдат отворени за разширение, но затворени за модификация. В резултат на това, когато бизнес изискванията се променят (условието на задачата се надгради), тогава обектът може да бъде разширен, но не и модифициран.
Пример:
public class Guitar {
private String make;
private String model;
private int volume;
//Constructors, getters & setters
}
В началото този клас описва класическа китара, но след време се налага добавянето за декорация.
Възможно е добавянето на поле за декорация в клас “Китара”, но това би довело и до други промени.
Вместо това е възможно разширяването на съществуващия клас Китара:
public class SuperCoolGuitarWithFlames extends Guitar {
private String flameColor;
//constructor, getters + setters
}
По този начин е сигурно, че досегашното приложение няма да бъде засегнато, а само ще бъде разширена функционалността.
Интерфейсите са един от начините за спазване на този принцип.
1. Несъответствие
Калкулатор за събиране и изваждане.
public interface CalculatorOperation {}
Клас ****“Добавяне**”, който би събрал две числа и би имплементирал **CalculatorOperation**:**
public class Addition implements CalculatorOperation {
private double left;
private double right;
private double result = 0.0;
public Addition(double left, double right) {
this.left = left;
this.right = right;
}
// getters and setters
}
Необходимо е декларирането и на клас Изваждане:
public class Subtraction implements CalculatorOperation {
private double left;
private double right;
private double result = 0.0;
public Subtraction(double left, double right) {
this.left = left;
this.right = right;
}
// getters and setters
}
Клас за изпълнение на операциите:
public class Calculator {
public void calculate(CalculatorOperation operation) {
if (operation == null) {
throw new InvalidParameterException("Can not perform operation");
}
if (operation instanceof Addition) {
Addition addition = (Addition) operation;
addition.setResult(addition.getLeft() + addition.getRight());
} else if (operation instanceof Subtraction) {
Subtraction subtraction = (Subtraction) operation;
subtraction.setResult(subtraction.getLeft() - subtraction.getRight());
}
}
}
Въпреки че това може да изглежда добре, това не е добър пример за OCP. Добавянето на нови операции би довело до промяна в съществуващия метод изчисляване на клас Калкулатор.
Това означава, че този код не е съвместим с OCP.
2. Съвместими с OCP
Горният пример може да бъде рефакторирано с цел спазване на OCP. Необходимо е методът изчисляване да бъде сложен на по-абстрактно ниво.
Едно възможно решение е всяка операция да бъде делегирана в съответен клас:
public interface CalculatorOperation {
void perform();
}
public class Addition implements CalculatorOperation {
private double left;
private double right;
private double result;
// constructor, getters and setters
@Override
public void perform() {
result = left + right;
}
}
Добавянето на нова функционалност се свежда до създаването на нов клас, имплементиращ същия интерфейс:
public class Division implements CalculatorOperation {
private double left;
private double right;
private double result;
// constructor, getters and setters
@Override
public void perform() {
if (right != 0) {
result = left / right;
}
}
}
По този начин няма да е необходимо класа ****Калкулатор** да бъде модифициран при добавяне на нова операция:**
public class Calculator {
public void calculate(CalculatorOperation operation) {
if (operation == null) {
throw new InvalidParameterException("Cannot perform operation");
}
operation.perform();
}
}
Така класът е затворен за модификация, но отворен за разширение.
3. Заключение
Принципа на отворени за разширение и затворени за модификация обекти подобрява работата върху проектите, като намалява грешките, които могат да възникнат при модифициране на класове и улеснява тестването като капсулира вече съществуващата логика и предоставя възможност за концентрация върху новата логика на приложението.