Инкапсуляция
Инкапсуляция объединяет данные с операциями и скрывает решения, которые клиенту не нужны. Практический результат — объект сам сохраняет свои инварианты.
public final class Account {
private long balance;
public void deposit(long amount) {
if (amount <= 0) {
throw new IllegalArgumentException("amount must be positive");
}
balance = Math.addExact(balance, amount);
}
public long balance() {
return balance;
}
}Если сделать balance публичным или дать безусловный setter, любой клиент сможет записать отрицательное значение. Метод deposit выражает допустимую операцию, проверяет условие и скрывает представление.
Инкапсуляция не равна «все поля private»
class WeakModel {
private List<String> items;
public List<String> getItems() {
return items; // внутреннее состояние всё равно раскрыто
}
}Нужны защитная копия, неизменяемое представление или операции уровня предметной области. Сокрытие данных — часть инкапсуляции, но важен и устойчивый контракт.
Границы
- класс/объект;
- пакет и package-private API;
- модуль и экспортируемые пакеты;
- сервис или компонент с протоколом.
Правильная граница помещает изменяемое решение за небольшим интерфейсом. Избыточные getters/setters превращают объект в структуру данных и переносят инварианты во все вызывающие места.
Ошибки
- возвращать изменяемую внутреннюю коллекцию;
- давать setter для каждого поля без бизнес-смысла;
- прятать непредсказуемые побочные эффекты за безобидным getter;
- считать reflection обычным способом обхода контракта.
На собеседовании
Инкапсуляция — это private? Это инструмент; цель — скрыть решение и защитить инварианты.
Почему setter может быть плох? Он разрешает изменение без контекста и проверки допустимой операции.
Как вернуть коллекцию безопасно? Неизменяемым представлением или защитной копией с учётом изменяемости элементов.
Связано: Модификаторы доступа, Immutable классы и данные, Основы ООП.