OCP - Open-Closed Principle

Принцип „отворен за разширение, затворен за промяна“

Принципът Open/Closed гласи, че софтуерните елементи трябва да бъдат отворени за разширение, но затворени за промяна. Това означава, че при добавяне на нова функционалност е желателно да се добавя нов код, без да се променя вече съществуващият и тестван код.

Проблем

Нека имаме калкулатор, който изпълнява различни операции.

public interface CalculatorOperation {
}
public class Addition implements CalculatorOperation {

    private double left;
    private double right;
    private double result;

    public Addition(double left, double right) {
        this.left = left;
        this.right = right;
    }

    public double getLeft() {
        return left;
    }

    public double getRight() {
        return right;
    }

    public void setResult(double result) {
        this.result = result;
    }

    public double getResult() {
        return result;
    }
}
public class Subtraction implements CalculatorOperation {

    private double left;
    private double right;
    private double result;

    public Subtraction(double left, double right) {
        this.left = left;
        this.right = right;
    }

    public double getLeft() {
        return left;
    }

    public double getRight() {
        return right;
    }

    public void setResult(double result) {
        this.result = result;
    }

    public double getResult() {
        return result;
    }
}
public class Calculator {

    public void calculate(CalculatorOperation 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. Ако бъде добавена нова операция, например умножение или деление, класът Calculator трябва да бъде променян.

Решение

Възможна е реализация, при която операцията сама да знае как да се изпълни.

public interface CalculatorOperation {

    void perform();
}
public class Addition implements CalculatorOperation {

    private double left;
    private double right;
    private double result;

    public Addition(double left, double right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public void perform() {
        result = left + right;
    }

    public double getResult() {
        return result;
    }
}
public class Subtraction implements CalculatorOperation {

    private double left;
    private double right;
    private double result;

    public Subtraction(double left, double right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public void perform() {
        result = left - right;
    }

    public double getResult() {
        return result;
    }
}
public class Calculator {

    public void calculate(CalculatorOperation operation) {
        operation.perform();
    }
}

Добавянето на нова операция вече не изисква промяна в класа Calculator.

public class Division implements CalculatorOperation {

    private double left;
    private double right;
    private double result;

    public Division(double left, double right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public void perform() {
        if (right == 0) {
            throw new ArithmeticException("Division by zero.");
        }

        result = left / right;
    }

    public double getResult() {
        return result;
    }
}

Предимства

Прилагането на OCP води до:

  • по-лесно добавяне на нова функционалност;
  • по-малък риск от грешки в съществуващ код;
  • по-добра разширяемост;
  • по-лесно тестване на отделните реализации.

Недостатъци / трудности

OCP често изисква използване на абстракции, интерфейси и допълнителни класове. При малки задачи това може да направи кода по-сложен от необходимото.

Приложение

OCP се използва при:

  • добавяне на нови операции;
  • работа с различни стратегии;
  • плъгини и разширения;
  • шаблони като Strategy, Factory Method, Abstract Factory, Decorator.

This site uses Just the Docs, a documentation theme for Jekyll.