Изменяемость, побочные эффекты и неизменяемые данные
Структуры в коде — Структуры данных; persistence — ORM.
Изменяемость — частый источник непредсказуемости. В духе 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. Указатель тем — Справочник тем чистого кода.