«1. Обзор
В этом руководстве мы обсудим принцип открытости/закрытости (OCP) как один из SOLID-принципов объектно-ориентированного программирования.
В целом, мы подробно рассмотрим, что это за принцип и как его реализовать при разработке нашего программного обеспечения.
2. Принцип открытости/закрытости
Как следует из названия, этот принцип гласит, что программные объекты должны быть открыты для расширения, но закрыты для модификации. В результате при изменении бизнес-требований объект может быть расширен, но не изменен.
На иллюстрации ниже мы сосредоточимся на том, как интерфейсы являются одним из способов следования OCP.
2.1. Несовместимо
Предположим, мы создаем приложение-калькулятор, которое может иметь несколько операций, таких как сложение и вычитание.
Прежде всего, мы определим интерфейс верхнего уровня – CalculatorOperation:
public interface CalculatorOperation {}
Давайте определим класс Addition, который будет добавлять два числа и реализовывать 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
}
As На данный момент у нас есть только один класс Addition, поэтому нам нужно определить еще один класс с именем Subtraction:
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. Когда появляется новое требование по добавлению функций умножения или деления, у нас нет иного пути, кроме как изменить метод calculate класса Calculator.
Следовательно, мы можем сказать, что этот код не совместим с OCP.
2.2. Совместимость с OCP
Как мы видели, наше приложение-калькулятор еще не совместимо с OCP. Код в методе вычисления будет меняться с каждым входящим запросом на поддержку новой операции. Итак, нам нужно извлечь этот код и поместить его на уровень абстракции.
Одним из решений является делегирование каждой операции соответствующему классу:
public interface CalculatorOperation {
void perform();
}
В результате класс Addition может реализовать логику сложения двух чисел:
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;
}
}
Аналогично, обновленный класс Subtraction будет имеют схожую логику. И аналогично сложению и вычитанию, в качестве нового запроса на изменение мы могли бы реализовать логику деления:
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;
}
}
}
И, наконец, нашему классу Calculator не нужно реализовывать новую логику, поскольку мы вводим новые операторы:
public class Calculator {
public void calculate(CalculatorOperation operation) {
if (operation == null) {
throw new InvalidParameterException("Cannot perform operation");
}
operation.perform();
}
}
~ ~~ Таким образом, класс закрыт для модификации, но открыт для расширения.
3. Заключение
В этом уроке мы узнали, что такое OCP по определению, а затем подробно остановились на этом определении. Затем мы увидели пример простого приложения-калькулятора, дизайн которого имел недостатки. Наконец, мы улучшили дизайн, сделав его единым с OCP.
Как всегда, код доступен на GitHub.