> For the complete documentation index, see [llms.txt](https://opu.gitbook.io/oop/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://opu.gitbook.io/oop/first-term/labs/laboratornaya-rabota-3/inkapsulyaciya.md).

# Инкапсуляция

Инкапсуляция в Java реализована с помощью использования модификаторов доступа.

Язык Java предоставляет несколько уровней защиты, которые позволяет настраивать область видимости данных и методов. В Java имеется четыре категории видимости элементов класса:

* `private`– члены класса доступны только членам данного класса. Всё что объявлено `private`, доступно только конструкторам и методам внутри класса и нигде больше. Они выполняют служебную или вспомогательную роль в пределах класса и их функциональность не предназначена для внешнего пользования. Закрытие (`private`) полей обеспечивает инкапсуляцию;
* по умолчанию (package-private) – члены класса доступны классам, которые находятся в этом же пакете;
* `protected`– члены класса доступны классам, находящимся в том же пакете, и подклассам – в других пакетах;
* `public`– члены класса доступны для всех классов в этом и других пакетах.

Модификатор класса указывается перед остальной частью описания типа отдельного члена класса. Это означает, что именно с него должен начинаться оператор объявления класса.

```java
public String errMessage;
private AccountBalance balance;

private boolean isError(byte status) {}
public class Account {}
```

Когда член класса обозначается модификатором доступа `public`, он становится доступным для любого другого кода в программе, включая и методы, определенные в других классах.

Когда член класса обозначается модификатором `private`, он может быть доступен только другим членам этого класса. Следовательно, методы из других классов не имеют доступа к закрытому члену класса.

При отсутствии модификатора доступа, члены класса доступны другим членам класса, который находится в этом же пакете.

Модификатор доступа `protected` связан с использованием механизма наследования и будет рассмотрен позже.

Модификатор доступа указывается перед остальной частью описания типа отдельного члена класса (то есть, именно с модификатора доступа начинается объявление члена класса).

Член класса (переменная, конструктор, методы), объявленный `public`, доступен из любого метода вне класса.

Всё что объявлено `private`, доступно только конструкторам и методам внутри класса и нигде больше. Они выполняют служебную или вспомогательную роль в пределах класса и их функциональность не предназначена для внешнего пользования. Закрытие (`private`) полей обеспечивает инкапсуляцию.

### Сокрытие полей класса

В подавляющем большинстве случаев, поля класса объявляются как `private` (это не касается статических переменных и констант, там ситуация может быть другая). Должны быть веские основания объявить поле класса общедоступным. Манипулирование данными должно осуществляться только с помощью методов.

Для того чтобы дать возможность получить доступ к переменной или дать возможность изменить ее значение, объявляют специальные методы, которые называются "**геттерами**" и "**сеттерами**".

**Геттер** возвращает значение приватного поля, тогда как **сеттер** меняет значение приватного поля (новое значение передается в качестве аргумента метода).

Хотя сигнатура и имена геттеров и сеттеров могут быть любыми, приучите себя соблюдать строгий шаблон для объявления геттеров и сеттеров.

Геттер должен иметь префикс `get`, после которого идет название поля с большой буквы. Геттер, как правило, не имеет входных аргументов.

Сеттер должен иметь префикс `set`, после которого идет название поля с большой буквы. Сеттер принимает на вход новое значение поля. Возвращаемый тип, как правило, `void`.

{% code title="Account.java" %}

```java
public class Account {

    private double balance;

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }
}
```

{% endcode %}

{% hint style="info" %}
Большинство IDE для Java имеют механизм для генерации геттеров и сеттеров. В IntelliJ IDEA нажмите комбинацию `Alt+Insert` находясь в окне редактирования java-файла. Откроется контекстное меню **Generate**, где вы можете выбрать генерацию геттера и сеттера, после чего указать поля, для которых необходимо сгенерировать методы.
{% endhint %}

## 5. Пример использования инкапсуляции

Представим, что нам необходимо создать класс «Корзина» (`Cart`), который хранит в себе набор объектов класса «Товар» (`Item`).

Какие методы «Корзина» должна предоставлять для внешнего использования? Это могут быть, например, методы «Добавить товар», «Убрать последний добавленный товар», «Подсчет суммы цен товаров в корзине», «Повышение цен в корзине на N процентов» и «Снижение цен в корзине на N процентов».

| Название метода                               | Описание                                                                                                   |
| --------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `public Cart(int capacity)`                   | Конструктор с 1 параметром – максимальным количеством товаров в корзине.                                   |
| `public boolean addItem(Item item)`           | Добавление товара в корзину. Возвращает успешность операции.                                               |
| `public Item deleteLastAddedItem()`           | Удаление последнего добавленного товара в корзину. Возвращает удаленный товар.                             |
| `public double calculateItemPrices()`         | Подсчет суммы цен всех товаров в корзине.                                                                  |
| `public void raiseItemPrices(double percent)` | Поднять цены товаров в корзине на определенный процент (значение процента передается как аргумент метода). |
| `public void cutItemPrices(double percent)`   | Снизить цены товаров в корзине на определенный процент (значение процента передается как аргумент метода). |

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

```java
Cart cart = new Cart();
cart.addItem(new Item("Клавиатура", 2000));
```

Перечень этих публичных методов и составляет **интерфейс** класса – то есть, с помощью этих методов объект класса будет взаимодействовать с внешним миром.&#x20;

Эти методы имеют вполне четко определенные входные аргументы и могут возвращать значения четко определенных типов, и никак иначе. По аналогии с этим, поворот колес автомобиля осуществляется четко определенным образом – поворотом руля, и бензин надо заливать в четко определенное отверстие крышки бензобака, а не как-то еще.

То – как будет реализовано хранение товаров в корзине – это внутренняя логика класса и она не должна быть доступна внешнему миру, она должна быть скрыта от внешнего вмешательства. Другие классы, которые будут использовать объекты класса `Cart`не должны знать и не должны иметь доступ к тому – как там «внутри» реализовано хранение товаров, подсчет цен и изменение цены на определенный процент и так далее, они могут только лишь использовать предоставленные им публичные методы. Давайте реализуем «Корзину» с помощью структуры «стек», которая, в свою очередь, реализована обычным массивом.

{% code title="Cart.java" %}

```java
public class Cart {

    private Item[] stack; // массив для реализации стека
    private int topIndex; // указатель на вершину стека

    // При создании корзины мы должны
    // указать максимальное количество элементов
    // в корзине
    public Cart(int capacity) {
        stack = new Item[capacity];
        topIndex = -1;
    }

    // Добавление нового товара в корзину
    public boolean addItem(Item item) {
        return push(item);
    }

    // Приватный метод, который реализует добавление в стек
    private boolean push (Item item) {
        // Добавляем товар в стек
        return true; // или false если не стек переполнен
    }
    
    // Удаление последнего добавленного товара в корзину
    public Item deleteLastAddedItem() {
        return pop();
    }

    // Приватный метод, который реализует извлечение из стека
    private Item pop() {
        return new Item(); // Извлеченный из стека товар
    }
}
```

{% endcode %}

Как мы видим, массив с товарами, указать на вершину стек объявлены как `private`члены класса. Это значит, что мы не можем получить к ним доступ извне – они доступны только внутри данного класса.

```java
Cart cart = new Cart();
cart.addItem(new Item("Клавиатура", 2000));

// Данная инструкция вызовет ошибку компиляции
cart.topIndex = 4;
```

Программиста, который будет использовать класс `Cart`, не должна волновать ситуация с переполнением стека, с попыткой извлечь элемент из пустого стека, он не должен следить за указателем на вершину стека, он даже не должен знать что это стек.&#x20;

Для него объект класса `Cart` это некоторый объект, который предоставляет «услугу» в виде корзины товаров и с этой корзиной можно работать с помощью определенных публичных методов.&#x20;

В дальнейшем мы можем переделать класс `Cart` и поменять внутреннюю реализацию. Мы можем использовать структуру "очередь", мы можем использовать коллекции, мы можем иначе реализовать операции добавления и удаления элемента в стеке, но если мы сохраним интерфейс класса неизменным, то для внешнего мира эти изменения внутренней логики не будут важны и если мы поменяем внутреннюю логику одного небольшого участка программы, то вся остальная программа будет работать так же.
