Основы ООП
Модель
Объектно-ориентированная программа строится из объектов с идентичностью, состоянием и поведением. Класс задаёт тип и правила допустимого состояния, а объект является конкретным экземпляром.
Сильная объектная модель не просто хранит поля: она не позволяет обойти инварианты.
final class Account {
private BigDecimal balance;
Account(BigDecimal initial) {
if (initial.signum() < 0) {
throw new IllegalArgumentException("negative balance");
}
balance = initial;
}
void withdraw(BigDecimal amount) {
if (amount.signum() <= 0 || balance.compareTo(amount) < 0) {
throw new IllegalArgumentException("invalid amount");
}
balance = balance.subtract(amount);
}
}Вместо публичного setBalance объект предоставляет операцию, которая сохраняет правило «баланс не отрицателен».
Четыре базовых механизма
Инкапсуляция
Состояние и детали реализации скрыты за контрактом. Это не просто private, а контроль способов изменения объекта. См. Инкапсуляция.
Абстракция
Тип показывает существенные свойства и скрывает лишние детали. List выражает упорядоченную последовательность, не заставляя пользователя знать устройство ArrayList.
Наследование
Подтип наследует доступное поведение базового типа и должен сохранять его контракт. Наследование выражает отношение «является» и создаёт сильную связь. См. Наследование.
Полиморфизм
Код работает через общий тип, а реализация выбирается по реальному объекту:
interface PricePolicy {
BigDecimal apply(BigDecimal base);
}
BigDecimal finalPrice(PricePolicy policy, BigDecimal base) {
return policy.apply(base);
}Подробно: Полиморфизм.
Композиция
Композиция выражает отношение «содержит/использует»:
final class CheckoutService {
private final PricePolicy pricePolicy;
CheckoutService(PricePolicy pricePolicy) {
this.pricePolicy = pricePolicy;
}
}Замена зависимости не требует строить новую иерархию. Поэтому композиция часто гибче наследования, но это не абсолютный запрет на наследование: оно уместно, когда подтип действительно соблюдает контракт родителя.
Entity и value object
- Entity важна своей стабильной идентичностью и меняющимся состоянием.
- Value object определяется значением, обычно неизменяем и сравнивается по полям.
record Money(BigDecimal amount, Currency currency) {}Не всякий DTO должен становиться «богатым» объектом; модель должна соответствовать задаче и границам системы.
Ловушки
- Сводить инкапсуляцию к генерации getters/setters.
- Использовать наследование только ради повторного использования кода.
- Создавать «god object», который знает и делает всё.
- Выносить каждую строку в отдельный интерфейс без реальной вариативности.
- Путать объектную модель с точной копией предметов реального мира.
- Нарушать контракт базового типа в наследнике.
Вопросы на собеседовании
- Что такое идентичность, состояние и поведение объекта?
- Чем инкапсуляция отличается от одного лишь
private? - Что даёт динамический полиморфизм?
- Когда композиция предпочтительнее наследования?
- Чем entity отличается от value object?
- Почему публичные setters могут разрушать инварианты?
Связанные заметки
Объекты Java · Класс Object · Инкапсуляция · Наследование · Полиморфизм · Абстрактные классы · Интерфейсы Java