Ответ
Контрактное программирование (DbC) — это парадигма, где взаимодействие между компонентами (например, клиентом и методом) формализуется в виде контракта, состоящего из условий.
Три основных типа условий:
- Предусловия (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"); } // Логика перевода } - Постусловия (Postconditions): Условия, гарантированные после выполнения метода. Отвечают за корректность результата и состояния. Ответственность метода.
public BigDecimal withdraw(BigDecimal amount) { BigDecimal oldBalance = this.balance; // ... логика снятия ... // Постусловие: новый баланс должен быть меньше или равен старому на сумму снятия assert this.balance.compareTo(oldBalance.subtract(amount)) == 0 : "Balance incorrect after withdrawal"; return amount; } - Инварианты (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 делает код более надежным, самодокументируемым и помогает выявлять ошибки на самой ранней стадии — при нарушении контракта.
Ответ 18+ 🔞
О, слушай, а вот есть такая штука — контрактное программирование. Это когда ты с кодом не просто так, а как с бухгалтером на зарплате: всё по пунктам, всё по договору, блядь. Никаких «ой, я думал, ты передашь нормальное число».
Представь, что метод — это такой занюханный клуб, а ты — вышибала. Контракт — это твои правила на входе.
Три кита, на которых всё держится, пока не проебалось:
-
Предусловия (Preусловия, Preconditions): Это что должен сделать клиент, прежде чем сунуться в метод. Твоя ответственность — проверить, не пришёл ли какой-нибудь мудак с пустыми руками.
public void transferMoney(Account from, Account to, BigDecimal summa) { // Вот тут ты стоишь на входе и проверяешь документы Objects.requireNonNull(from, "Откуда переводим, пидор? С неба что ли?"); Objects.requireNonNull(to, "Кому переводим, вникуда?"); if (summa.compareTo(BigDecimal.ZERO) <= 0) { throw new IllegalArgumentException("Ты мне ноль или минус принёс? Иди нахуй с таким переводом!"); } // Если все проверки прошли — проходи, дорогой, внутри уже разберёмся. }Если клиент нарушил — это его проблема, его исключение и его позор. Ты предупредил.
-
Постусловия (Postconditions): Это что метод обязан сделать для клиента после того, как отработает. Его ответственность. Как будто ты заказал пиццу, а тебе привезли коробку с крошками и запиской «извините, кот сожрал». Непорядок.
public BigDecimal snatDengi(BigDecimal summa) { BigDecimal stariyBalance = this.balance; // Запоминаем, сколько было до всей движухи // ... тут магия снятия, списывания, возможно, с комиссией... // А теперь постусловие: проверяем, не наебал ли я клиента? // Новый баланс должен быть РОВНО старый минус сумма снятия. assert this.balance.compareTo(stariyBalance.subtract(summa)) == 0 : "Ёпта, баланс посчитался криво! Где-то хуйня!"; return summa; }Если метод нарушил своё постусловие — это его косяк, его внутренний пиздец.
assertтут как внутренний аудитор, который орёт, когда видит хуйню. -
Инварианты (Invariants): Это священные коровы, которые должны быть истиной ВСЕГДА, в любой момент жизни объекта. До метода, после метода, во время метода (хотя во время могут и нарушаться, главное — к концу восстановиться).
class Account { private BigDecimal balance; // Священный инвариант: баланс НИКОГДА не должен уходить в минус. // Не «иногда», не «по пятницам», а НИКОГДА. private void proverkaInvarianta() { assert balance.compareTo(BigDecimal.ZERO) >= 0 : "Баланс ушёл в минус! Всё, пиздец, система посыпалась!"; } // И эту проверку надо вызывать в критичных местах. }Инвариант — это как здоровье печени. Пока оно есть — ты его не замечаешь. Как только нарушилось — всё, ёперный театр, цирроз и смерть объекта.
Чем это всё, блядь, полезно? А тем, что когда что-то ломается, ты сразу понимаешь, кто накосячил: клиент (предусловие), метод (постусловие) или объект вообще поехал кукухой (инвариант). Вместо трёх дней дебага в попытках понять, где же эта сука ошибка зарылась. Всё по контракту, всё честно. И код сам себе документация, ебать его в сраку.