Архив за месяц: Март 2021

Иммутабельность

Иммутабельность необходима для избежания «глупых» ошибок в программах, которые, думаю, всегда связаны со случайным изменением передаваемого объекта в потоке выполнения программы. Часто бывает, что при работе с датами, деньгами, координатами в пространстве и подобными вещами удобно создать класс-представление содержащий набор значений и использовать его как тип данных. Вот к нему и стоит предъявить требование неизменяемости. Хотя, во время реализации этого требования, может прийти мысль об избыточности. Я сейчас говорю о избыточности при выделении памяти во время конструирования копии объекта. Есть шаблоны проектирования, в которых обеспечивать неизменяемость уже стало хорошим тоном, например, М. Фаулер предписывает делать Value Objects иммутабельными всегда и приводит в своих статьях примеры вида: имеется базовая дата в виде VO, присваиваем её переменной, рассчитывая работать с этой датой (вызовем метод add) и… базовая дата изменилась! Вся магия произошла в момент присваивания даты переменной — это один и тот же объект. Обычно этого, не происходит в функциональных языках, а в ОО языках, скорее всего, произойдет. Есть статья об этой проблеме в которой описано как именно технически реализовать иммутабельность.

Выше я говорил про паттерн Value object, но есть ведь похожий паттерн Data Transfer Object, нужно ли в нём стараться обеспечивать неизменяемость? Почему вообще встал этот вопрос? Ведь многие скажут что эти шаблоны проектирования — одно и тоже. На сколько я знаю, это разные шаблоны, хоть и похожи но отличаются они тем, что DTO служит для коммуникации между процессами или передачи данных из метода в метод. То есть как преимущество мы получаем меньшее количество параметров (множество переменных упаковывается в объект), некоторую типизацию, возможность сериализации параметров, но не нужно рассматривать его как неделимый тип данных (он не является датой, суммой, координатами …), а ещё не стоит добавлять туда никакую логику. А вот VO наоборот, является «неделимым», целостным типом данных, например сумма денег вместе с валютой или координаты, при этом сюда можно добавить метод сравнения типов между собой или что-то подобное. Но это не железное правило, потому что объект типа Request, содержащий данные запроса от клиента к серверу, я бы отнес к DTO и при этом сделал бы неизменяемым. Я бы не хотел чтобы запрос клиента (Request) в процессе работы моей программы как-то изменился, это может внести ошибки.