# OCP - Open-Closed Principle

**Както подсказва името, този принцип гласи, че софтуерните обекти трябва да бъдат отворени за разширение, но затворени за модификация.** В резултат на това, когато бизнес изискванията се променят (условието на задачата се надгради) тогава обектът може да бъде разширен, но не и модифициран.

Нека да разгледаме следния пример:

```
public class Guitar {
    private String make;
    private String model;
    private int volume;

    //Constructors, getters & setters
}
```

Започваме с класическа итара, но след някоплко месеца решаваме, че една китара брандирана с пламаци ще изглежда по-рокендрол

‎В този момент може да е съблазнително просто да отворим класа ‎*‎"Китара‎*‎" и да добавим поле за пламъка — но кой знае какви грешки може да доведе това в нашия клас.‎

‎Вместо това нека ‎**‎се придържаме към принципа на отворените за разширение класове и просто да разширим класа си&#x20;*****Китара‎***&#x200E;:‎

```
public class SuperCoolGuitarWithFlames extends Guitar {

    private String flameColor;
 
    //constructor, getters + setters
}
```

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

**Ще разгледаме как интерфейсите са един от начините да следвате на този принцип**

### **1.** Несъответствие

**Нека разгледаме изграждането на приложение за калкулатор, което може да има няколко операции, като например добавяне и изваждане.**

На първо място, ще определим интерфейс от най-високо ниво – *CalculatorOperation*:

```
public interface CalculatorOperation {}
```

**Нека създадем клас&#x20;*****"Добавяне*****", който би събрал две числа и би разшерил&#x20;*****CalculatorOperation*****:**

```java
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

}
```

От сега имаме само един клас *Добавяне,* така че трябва да определим друг клас на име *Изваждане*:

```java
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
}
```

**Нека сега определим нашия основен клас, който ще изпълни нашите калкулаторни операции:**

```java
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. Кодът в метода *на изчисляване* ще се промени с всяко входящо ново искане за поддръжка на операция. Така че, трябва да извлечем този код и да го сложим в абстракционен слой.

Едно решение е всяка операция да бъде делегирана в съответния им клас:

```java
public interface CalculatorOperation {
    void perform();
}
```

**В резултат на това класът&#x20;*****"Добавяне"*****&#x20;би могъл да внедри логиката на добавяне на два номера:**

```java
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;
    }
}
```

По същия начин актуализиран клас *на Изваждане* би имал подобна логика. И подобно на *Добавяне* и *Изваждане*, като настъпи нова задача за промяна, бихме могли да внедрим логиката на *разделението*:

```java
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;
        }
    }
}
```

**И накрая, нашият клас&#x20;*****Калкулатор*****&#x20;няма нужда да прилага нова логика, когато въвеждаме нови оператори:**

```java
public class Calculator {

    public void calculate(CalculatorOperation operation) {
        if (operation == null) {
            throw new InvalidParameterException("Cannot perform operation");
        }
        operation.perform();
    }
}
```

Така класът е *затворен* за модификация, но *отворен* за разширение.

### 3. Заключение

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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://programmingfundamental.gitbook.io/programmingwithjava/obektno-orientirano-programirane-2-chast/laboratorno-uprazhnenie-1/ocp-open-closed-principle.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
