Что такое контрактное программирование (Design by Contract) в Java?

«Что такое контрактное программирование (Design by Contract) в Java?» — вопрос из категории Other, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Контрактное программирование (DbC) — это парадигма, где взаимодействие между компонентами (например, клиентом и методом) формализуется в виде контракта, состоящего из условий.

Три основных типа условий:

  1. Предусловия (Preconditions): Условия, которые должны быть истинными перед вызовом метода. Отвечают за корректность входных данных. Ответственность клиента.
    // Пример с явной проверкой (рекомендуется)
    public void transfer(Account from, Account to, BigDecimal amount) {
        Objects.requireNonNull(from, "Source account cannot be null");
        Objects.requireNonNull(to, "Target account cannot be null");
        if (amount.compareTo(BigDecimal.ZERO) <= 0) {
            throw new IllegalArgumentException("Amount must be positive");
        }
        // Логика перевода
    }
  2. Постусловия (Postconditions): Условия, гарантированные после выполнения метода. Отвечают за корректность результата и состояния. Ответственность метода.
    public BigDecimal withdraw(BigDecimal amount) {
        BigDecimal oldBalance = this.balance;
        // ... логика снятия ...
        // Постусловие: новый баланс должен быть меньше или равен старому на сумму снятия
        assert this.balance.compareTo(oldBalance.subtract(amount)) == 0 : "Balance incorrect after withdrawal";
        return amount;
    }
  3. Инварианты (Invariants): Условия, истинные на протяжении всего времени жизни объекта (и до, и после вызова любого публичного метода).
    class Account {
        private BigDecimal balance;
        // Инвариант: баланс никогда не должен быть отрицательным
        private void checkInvariant() {
            assert balance.compareTo(BigDecimal.ZERO) >= 0 : "Balance invariant violated";
        }
    }

Инструменты в Java:

  • Ключевое слово assert: Для проверки постусловий и инвариантов (требует флага -ea при запуске JVM).
  • Библиотеки: java.util.Objects.requireNonNull(), Guava Preconditions, Spring Framework Assert — для проверки предусловий с явными исключениями.

Почему это важно: DbC делает код более надежным, самодокументируемым и помогает выявлять ошибки на самой ранней стадии — при нарушении контракта.