Изменяемость, побочные эффекты и неизменяемые данные
Изменяемость — частый источник непредсказуемости. В духе MAPPER объект меняет только то состояние, которое имеет смысл в предметной области, и делает это явно.
Зачем снижать изменяемость
| Проблема | Последствие |
|---|---|
| Общие мутабельные структуры | Гонки, «пропавшие» обновления в async |
| Сеттеры «на всё» | Инварианты легко нарушить извне |
| Ленивая инициализация полей | Скрытые зависимости и порядок вызовов |
| Побочные эффекты в «чистых» функциях | Тесты зависят от окружения |
Код читают чаще в режиме «что может измениться?», чем «какие тут поля». Чем меньше точек мутации, тем проще ревью и юнит-тесты.
Практики по языкам
JavaScript / TypeScript — const для ссылок, Object.freeze для конфигов, spread вместо мутации массива:
const next = [...items, newItem]; // вместо items.push в shared state
C# — init, readonly поля, record для DTO и value object:
public sealed record Money(decimal Amount, string Currency);
Python — @dataclass(frozen=True), неизменяемые кортежи для координат.
На практике: замена var на const где возможно; не объявлять переменные переменными (одна let на разные роли — запах); отказ от «изменяемых констант» (объект в const, но поля мутируются без дисциплины).
Существенные и несущественные изменения
Существенные характеристики задаются при создании (OrderId, дата рождения). Несущественные могут меняться через явные методы (rename, updateAddress). Случайный setEmail на сущности User — признак анемии (статья 6).
Побочные эффекты
Побочный эффект — изменение состояния вне текущей функции (глобал, синглтон, файл, сеть). Они же ухудшают тестируемость — см. глобальные сущности.
Полезные приёмы:
- передавать зависимости явно (порты/адаптеры);
- отделять команды от запросов (CQS, см. статью 1);
- I/O выносить на край гексагона.
Ленивая инициализация
Паттерн «если поле null — создать список» внутри геттера скрывает стоимость и ломает потокобезопасность. Лучше инициализировать в конструкторе или фабрике; ленивость — осознанно, с документированной синхронизацией.
Связь с параллелизмом
В параллельных вычислениях неизменяемые структуры снижают блокировки. В UI и вебе иммутабельное состояние (Redux, React state) упрощает отладку.
Функциональные языки (Haskell) делают неизменяемость нормой; в императивных — это дисциплина команды.
Краткий чек-лист
const/readonly/frozenтам, где состояние не должно меняться.- Существенные атрибуты задаются при создании, не через случайные сеттеры.
- Ленивая инициализация — только осознанно.
- Побочные эффекты вынесены на границы модуля.
Дальше: Декларативный стиль → Условия и null. Указатель тем — 13.
См. также
Другие статьи этого же раздела в боковом меню (как на странице "О разделе"). Именование, форматирование, комментарии, документация в коде и базовые принципы читаемости — практики, которые команда договаривается соблюдать каждый день. Цикломатическая сложность — одна из наиболее устойчиво применяемых метрик статического анализа программного кода, призванная количественно оценивать логическую структуру исполняемого модуля. Правило MAPPER (Model Abstract Partial Programmable Explaining Reality) — как сопоставлять реальность и код один к одному. Богатые объекты предметной области, value objects вместо string/int и антипаттерны DTO-оргии. Разделение «что» и «как», итерации, магические числа, callback hell и явные ошибки. Меньше if и switch, отказ от null, быстрый провал и полиморфизм вместо флагов. Singleton, god object, shotgun surgery, feature envy и глобальное состояние — симптомы и приёмы рефакторинга. Мёртвый код, лишние абстракции, отключённые предупреждения и отложенный рефакторинг. Приватные методы, flaky-тесты, assertTrue, моки и данные — качество тестового кода и связь с разделом тестирования. Пустые catch, исключения как goto, узкие try и сообщения для пользователя. Краткие итоги раздела "Культура кода". Вопросы перед отправкой кода в репозиторий — именование, принципы, тесты, ревью и безопасность. Ответы ищите в статьях раздела.Культура написания и поддержки кода
Цикломатическая сложность и читаемость кода
MAPPER — модель кода и предметная область
Анемичные модели и примитивная одержимость
Декларативный код — что и как
Условия, null и явные контракты
Связанность, глобалы и запахи модульности
YAGNI, быстрый провал и техдолг в коде
Тесты как часть культуры кода
Исключения и обработка ошибок в читаемом коде
Итоги
Чек-лист самопроверки