Публичные поля выглядят удобно ровно до первого бага.
account.balance = new BigDecimal("-100");
Код скомпилировался, объект изменился, бизнес-правило сломалось.
Никакой проверки, никакого контроля, никакой гарантии, что BankAccount остаётся в корректном состоянии.
Поэтому поле баланса не должно быть частью публичного API. Наружу можно отдать чтение, а изменение проводить через методы, где явно описаны правила:
private BigDecimal balance = BigDecimal.ZERO;
public BigDecimal getBalance() {
return balance;
}
public void withdraw(BigDecimal amount) {
if (amount.signum() <= 0) {
throw new IllegalArgumentException("amount must be positive");
}
if (balance.compareTo(amount) < 0) {
throw new IllegalStateException("insufficient funds");
}
balance = balance.subtract(amount);
}
Геттеры и сеттеры нужны не для того, чтобы механически обернуть каждое поле методами.
Смысл инкапсуляции в том, чтобы объект сам защищал свои инварианты: баланс не уходит в минус, сумма списания валидна, состояние нельзя сломать прямым присваиванием.
Если значение можно менять только по правилам, это уже не просто структура данных, а нормальная доменная модель.
