Перейти к основному содержимому

Условия, null и явные контракты

Разработчику

Три связанные темы — быстрый провал, операторы if и null — про одно: ветвления и «пустота» должны быть частью модели, а не размазаны по коду. Это продолжение MAPPER и дополнение к цикломатической сложности.


Принцип быстрого провала (Fail Fast)

Ошибочное состояние должно всплыть сразу, у границы системы, с понятным сообщением. Тихий return null или default в switch «на всякий случай» откладывает катастрофу.

public void Transfer(Money amount, Account target) {
if (amount.IsNegative)
throw new ArgumentException("Amount must be positive");
if (target is null)
throw new ArgumentNullException(nameof(target));
// ...
}

На практике:

  • предусловия вместо глубоких проверок внутри;
  • строгие типы параметров (не string там, где Email);
  • без default, если все варианты switch должны быть исчерпывающими;
  • не менять коллекцию во время обхода;
  • рефакторинг отдельно от новой функциональности (один PR — одна цель; см. легаси).

Зачем меньше if

Каждый if — дополнительный путь в графе потока управления. Цель не «запретить if», а убрать несущественные ветки:

ЗапахЛучше
Флаги isX, hasY + цепочка ifПолиморфизм, strategy, state
switch по типу/кодуТаблица обработчиков, объекты
Булевы переменные только для ifПредикат с именем или объект правила
Вложенные «стрелы»Guard clauses, ранний return
Сравнение с true/falseПрямое условие или именованный предикат
// До: жёстко запрограммированное правило
if (user.Age < 18 && movie.Rating == "AdultsOnly") { Deny(); }

// После: правило у объекта политики
movie.Rating.EnsureAllowedFor(user);

Подробный указатель приёмов для условий — 13.

Связь с Фаулером: Replace Conditional with Polymorphism, Decompose Conditional612.


Проблема null

Ссылки, допускающие null, часто смешивают три смысла: «нет значения», «неизвестно», «ошибка».

Альтернативы:

ПодходКогда
Null ObjectПустое поведение вместо null (NullLogger, пустой список)
Optional / MaybeЯвно «может отсутствовать» в API
Коллекция0 или 1 элемент вместо null ссылки
ИсключениеНевозможное состояние на границе
Result / EitherОжидаемый отказ без исключения
// Optional chaining маскирует дыру в модели
const city = user?.address?.city ?? "";

// Явная модель
const city = user.knownAddress().map(a => a.city()).orElse("");

Практики: Null Object; убрать лишние ?.; не хранить «неизвестное местоположение» как null GPS.

В C# 8+ — nullable reference types; в Kotlin — non-null по умолчанию. Всё равно нужна доменная дисциплина, а не только аннотации.


Декларативность условий

Разделяйте что проверяем и как исправляем — см. Декларативный стиль. Имя isEligibleForDiscount лучше, чем комментарий над if (a && !b || c).

Регулярные выражения и парсеры — с именованными шагами или готовыми типами (Email.parse), иначе условие становится нечитаемым «супом».


Метрики и ревью

  • Рост цикломатической сложности на diff — повод открыть статью 2.
  • Новые switch по enum без default — хорошо, если enum закрыт; иначе — риск тихого пропуска.
  • Каждый новый ? в TypeScript/C# — вопрос: есть ли понятие в домене?

Куда дальше


См. также

Другие статьи этого же раздела в боковом меню (как на странице "О разделе").