Перейти к основному содержимому

Принцип открытости/закрытости в Java

· 4 мин. чтения

1. Обзор

В этом руководстве мы обсудим принцип открытости/закрытости (OCP) как один из SOLID-принципов объектно-ориентированного программирования.

В целом, мы подробно рассмотрим, что это за принцип и как его реализовать при разработке нашего программного обеспечения.

2. Принцип открытия/закрытия

Как следует из названия, этот принцип гласит, что программные объекты должны быть открыты для расширения, но закрыты для модификации. В результате при изменении бизнес-требований объект может быть расширен, но не изменен.

На иллюстрации ниже мы сосредоточимся на том, как интерфейсы являются одним из способов следования OCP.

2.1. Не соответствует

Давайте представим, что мы создаем приложение-калькулятор, которое может иметь несколько операций, таких как сложение и вычитание.

Прежде всего, мы определим интерфейс верхнего уровня — CalculatorOperation :

public interface CalculatorOperation {}

Давайте определим класс Addition , который будет добавлять два числа и реализовывать C alculatorOperation :

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

}

На данный момент у нас есть только один класс 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 будет иметь аналогичную логику. И аналогично Addition and 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 .