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

Изменяемость, побочные эффекты и неизменяемые данные

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

Изменяемость — частый источник непредсказуемости. В духе MAPPER объект меняет только то состояние, которое имеет смысл в предметной области, и делает это явно.


Зачем снижать изменяемость

ПроблемаПоследствие
Общие мутабельные структурыГонки, «пропавшие» обновления в async
Сеттеры «на всё»Инварианты легко нарушить извне
Ленивая инициализация полейСкрытые зависимости и порядок вызовов
Побочные эффекты в «чистых» функцияхТесты зависят от окружения

Код читают чаще в режиме «что может измениться?», чем «какие тут поля». Чем меньше точек мутации, тем проще ревью и юнит-тесты.


Практики по языкам

JavaScript / TypeScriptconst для ссылок, 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.


См. также

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