Как сделать класс immutable

Что такое mutable/immutable объекты и зачем они

Как сделать класс immutable

— Сегодня Билаабо расскажет тебе о mutable и immutable объектах.

Объекты, которые после создания можно изменить, называются изменяемыми или mutable.

Объекты, которые после их создания изменить нельзя, называются неизменяемыми или immutable.

— А от чего зависит, можно объект менять или нет?

— Человек, который пишет новый класс, может сделать объекты этого класса неизменяемыми. Например, можно скрыть все setter’ы — у объекта будет только конструктор и getter’ы, а, значит, после создания объекта поменять его уже будет нельзя.

— И какая от этого польза?

— У неизменяемых объектов много полезных свойств. Но можно выделить два, которые характерны практически для всех immutable-объектов:

1) Неизменяемые объекты можно реализовать значительно проще, чем изменяемые.

2) Неизменяемые объекты можно свободно использовать одновременно из разных нитей.

Чаще всего, когда разработчик решает написать immutable класс, он делает две версии этого класса — mutable и immutable.

— А в чем смысл писать два класса вместо одного?

— Иногда так выгоднее, когда неизменяемая версия объекта будет гораздо проще/быстрее чем изменяемая. Тогда и делают две версии. Это почти как ArrayList и LinkedList: оба — списки, но один оптимизирован для одних целей, второй — для других.

— Бывают также и чисто immutable классы, без их mutable версии.

— А если мне нужно что-то поменять в таком объекте? Что вообще можно сделать с неизменяемым объектом?

— Обычно immutable классы содержат различные методы, которые «как бы» меняют объект, но вместо изменения самого объекта эти методы просто создают новый объект и возвращают его.

Вот тебе несколько примеров:

Код на JavaОписание
В результате s содержит строку «moscow», а s2 — «MOSCOW»
Вот что происходит на самом деле:
Integer i = new Integer(1);
Integer j = i;
j = new Integer(i.getInt()+1);

Класс String — это immutable класс. Все объекты типа String — неизменяемые, что, однако, не мешает нам с ними работать. Например, метод toUpperCase() класса String преобразовывает строку в верхний регистр (заменяет все маленькие буквы на большие). Но этот метод не меняет саму строку, а возвращает новую строку, которая идентична первой, только все символы в верхнем регистре (большие).

Класс Integer — это тоже immutable класс. Все объекты типа Integer — неизменяемые. Каждый раз, когда мы изменяем объект Integer, на самом деле создается новый объект.

Источник

immutable-объекты и многопоточность

Доброго дня, уважаемые гуру Java. Анализирую вопросы, которые задают на собеседованиях. Пытаюсь более детально разобраться с темой immutable-объектов. К сожалению, те ресурсы, которые предлагает «Google» дают лишь очень общую информацию в стиле «immutable-объекты, это те объекты, которые не могут быть изменены в программе. Для того, чтобы объект был immutable используйте модификатор final». Как энциклопедические знания такое конечно можно сказать, на собеседовании, но думаю, там хотят слышать понимание более глубоких принципов. Кроме того, хочу написать мини-проект для лучшего понимания.

Можете ли Вы пояснить на примерах или подсказать адекватные источники, где раскрыты рекомендации по использованию неизменяемых объектов в контексте многопоточности?

2 ответа 2

Для начала, есть разница между immutable-объектом (то есть, неизменяемым), и final-ссылкой.

В случае же immutable-объекта объект после окончания конструктора не изменяется вообще. Одного лишь модификатора final для этого недостаточно, необходимо, чтобы все подбъекты были тоже неизменяемыми. Вы в принципе можете держать внутри ссылку на изменяемый объект, но обращаться с ним так, чтобы он не менялся.

Использование неизменяемых объектов даёт много выгод. Например, о таком объекте намного легче судить в ситуации, когда во многих частях программы есть ссылка на него (для изменяемого объекта, любая часть программы может вызвать мутирующую функцию в практически любой момент времени и из любого потока).

Но то, что для нас важно в контексте вопроса — неизменяемые объекты не требуют синхронизации при многопоточном доступе. Вот собственно и вся рекомендация: используйте неизменяемые объекты, и вам не придётся думать о том, что нужно, а что не нужно синхронизировать. Единственная возможная проблема — если вы внутри ещё не отработавшего конструктора публикуете ссылку на объект, через которую к нему может получить доступ кто-нибудь ещё, и увидеть объект в изменяющемся состоянии! (Это бывает не так уж и редко. Например, иногда программист хочет добавить объект в конструкторе в коллекцию всех объектов данного типа.)

Следует различать действительно неизменяемые объекты, и объекты, имеющие лишь интерфейс «только для чтения». При чтении объект тем не менее может менять свою внутреннюю структуру (например, кэшировать самый свежий запрос данных). Такие объекты не являются в строгом смысле неизменяемыми, и не могут быть использованы из разных потоков без предосторожностей. (Поэтому, если ваш объект включает другие объекты, убедитесь, что документация гарантирует их неизменяемость!)

Источник

Functional C#: Immutability

Это первая статья из небольшой серии, посвященной программированию на C# в функциональном стиле. Серия не про LINQ, как можно было бы подумать, а про более фундаментальные вещи. Навеяно F#-ом.

Immutability (неизменяемость)

Наибольшая проблема в мире enterprise разработки — это борьба со сложнотью. Читаемость кода — это пожалуй первое чего мы должны стараться достичь при написании любого более-менее сложного проекта. Без этого наша способность понимать код и принимать на основе этого разумные решения значительно ухудшается.

Помогают ли нам изменяемые объекты при чтении кода? Давайте рассмотрим пример:

Изменился ли queryObject к моменту поиска кастомеров во второй раз? Может быть, да. А может, и нет. Это зависит от того, был ли этот объект изменен методом AdjustSearchCriteria. Чтобы выяснить это, нам необходимо заглянуть внутрь этого метода, его сигнатура не дает нам достаточной информации.

Сравните это со следующим кодом:

В этом примере ясно, что AdjustSearchCriteria создает новый объект критерия и использует его в дальнейшем для нового поиска.

Так в чем проблема с изменяемыми структурами данных?

Как создавать неизменямые типы

В будущих версиях C# возможно появится ключевое слово immutable. С его помощью можно будет понимать является ли тип неизменямым просто глядя на его сигнатуру. Пока же нам приходится пользоваться тем, что есть.

Если вы имеете сравнительно простой класс, рассмотрите возможность сделать его неизменямым. Этот гайд-лайн коррелирует с понятием Value Objects.

Возьмем для примера класс ProductPile, описывающий какое-то количество продуктов на продажу:

Чтобы сделать его неизменяемым, мы можем пометить его свойства как read-only и добавить конструктор:

Теперь предположим, что вам необходимо уменьшать свойство Amount на единицу каждый раз когда вы продаете один из продуктов. Вместо того, чтобы изменять имеющийся объект, мы может создавать новый на основе имеющегося:

Ограничения

Конечно, каждая полезная практика имеет свою цену. В то время как небольшие классы пользуются преимуществами неизменяемости в полной мере, такой подход не всегда применим в случае с большими типами.

В первою очередь, неизменяемость несет в себе потенциальные проблемы с производительностью. Если объект довольно большой, необходимость создавать его копию при каждом изменении может стать проблемой.

Хорошим примером тут будут неизменяемые коллекции. Авторы учли потенциальные проблемы с производительностью и добавили специальный класс Builder, который позволяет изменять состояние коллекций. После того, как коллекция приведена к необходимому состоянию, ее можно финализировать, конвертировав в неизменяемую:

Заключение

В большинстве случаев, неизменяемые типы (особенно если они довольно просты) делают код лучше.

Источник

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

Как сделать класс immutable

Неизменяемым (англ. immutable) называется объект, состояние которого не может быть изменено после создания. Результатом любой модификации такого объекта всегда будет новый объект, при этом старый объект не изменится.

Речь не идет о глубоком копировании: если объект имеет вложенную структуру, то все вложенные объекты, не подвергшиеся модификации, будут переиспользованы.

В памяти объекты будут представлены следующим образом:

Как сделать класс immutable

Правда или ложь? Иммутабельные данные в JavaScript

Простое и быстрое отслеживание изменений

Эту возможность активно используют в связке с популярным нынче VirtualDOM (React, Mithril, Riot) для ускорения перерисовки web-страниц.

Внутри объектов bounds и style.font операции сравнения производить не нужно, так как они иммутабельны, и ссылки на них не изменились.

Безопаснее использовать и легче тестировать

Нередки случаи, когда переданные в функцию данные могут быть случайно испорчены, и отследить такие ситуации очень сложно.

Здесь иммутабельные данные спасли бы ситуацию. Функция sort была бы запрещена.

Или вернула бы новый отсортированный массив, не меняя старый:

Больший расход памяти

Каждый раз при модификации иммутабельного объекта создается его копия с необходимыми изменениями. Это приводит к большему расходу памяти, чем при работе с обычным объектом. Но поскольку иммутабельные объекты никогда не меняются, они могут быть реализованы с помощью стратегии, называемой «общие структуры» (structural sharing), которая порождает гораздо меньшую издержку в затратах на память, чем можно было бы ожидать. В сравнении со встроенными массивами и объектами издержка все еще будет существовать, но она будет иметь фиксированную величину и обычно может компенсироваться другим преимуществами, доступными благодаря неизменяемости.

Легче кешировать (мемоизировать)

В большинстве случаев кешировать легче не станет. Этот пример прояснит ситуацию:

Несмотря на то, что data.value из первого шага не отличается от data.value из последнего шага, сам объект data уже другой, и ссылка на него тоже изменилась.

Отсутствие побочных эффектов

Гарантий того, что функция станет чистой, или что у нее будут отсутствовать побочные эффекты — нет.

Ускорение кода. Больше простора для оптимизаций

Thread safety

JavaScript — однопоточный, и говорить тут особо не о чем. Многие путают асинхронность и многопоточность — это не одно и тоже.
По умолчанию есть только один поток, который асинхронно обслуживает очередь сообщений.
В браузере для многопоточности есть WebWorkers, но единственное возможное общение между потоками осуществляется через отправку строк или сериализованного JSON; к одним и тем же переменным из разных воркеров обратиться нельзя.

Возможности языка

Ключевое слово const

Использование const вместо var или let не говорит от том, что значение является константой или что оно иммутабельно (неизменяемо). Ключевое слово const просто указывает компилятору следить за тем, что переменной больше не будет присвоено никаких других значений.

В случае использования const современные JavaScript-движки могут выполнить ряд дополнительных оптимизаций.

Object.freeze

Метод Object.freeze замораживает объект. Это значит, что он предотвращает добавление новых свойств к объекту, удаление старых свойств из объекта и изменение существующих свойств или значения их атрибутов перечисляемости, настраиваемости и записываемости. В сущности, объект становится эффективно неизменным. Метод возвращает замороженный объект.

Сторонние библиотеки

Seamless-Immutable

Библиотека предлагает иммутабельные структуры данных, обратно совместимые с обычными массивами и объектами. То есть доступ к значениям по ключу или по индексу не будет отличаться от привычного, будут работать стандартные циклы, а также все это можно использовать в связке со специализированными высокопроизводительными библиотеками для манипуляций с данными, вроде Lodash или Underscore.

Некоторые браузеры, например Safari, имеют проблемы с производительностью при работе с замороженными при помощи Object.freeze объектами, так что в production сборке это отключено для увеличения производительности.

Immutable.js

Благодаря продвижению со стороны Facebook эта библиотека для работы с иммутабельными данными стала самой распространенной и популярной среди web-разработчиков. Она предоставляет следующие неизменяемые структуры данных:

Библиотека, которая привносит персистентные структуры данных из ClojureScript (Lists, Vectors, Maps и т.д.) в JavaScript.

Отличия от Immutable.js:

Проблемы при разработке, с которыми вы столкнетесь

Речь пойдет об использовании Immutable.js (с Mori все примерно также). В случае работы с Seamless-Immutable таких проблем у вас не возникнет из-за обратной совместимости с нативными структурами JavaScript.

Работа с серверным API

Дело в том, что в большинстве случаев серверное API принимает и возвращает данные в формате JSON, который соответствует стандартным объектам и массивам из JavaScript. Это значит, что нужно будет каким-то образом преобразовывать Immutable-данные в обычные и наоборот.

Immutable.js для конвертации обычных данных в иммутабельные предлагает следующую функцию:

где с помощью функции reviver можно добавлять собственные правила преобразования и управлять существующими.

Предположим, серверное API вернуло нам следующий объект:

Удобнее всего такой объект будет представить как OrderedMap. Напишем соответствующий reviver :

Предположим, нам нужно изменить данные и отправить их обратно на сервер:

Immutable.js для конвертации иммутабельных данных в обычные предлагает следующую функцию:

Можно, конечно, использовать при разработке только List и Map, но тогда зачем же все остальное? И в чем плюсы использования конкретно Immutable.js?

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

Если проект раньше работал с нативными структурами данных, то легко перейти на иммутабельные не получится. Придется переписывать весь код, который хоть как-то взаимодействует с данными.

Сериализация/Десериализация

То есть абсолютно бесполезны для сериализации/десериализации.

Существует сторонняя библиотека transit-immutable-js. Пример ее использования:

Производительность

Для тестирования производительности были написаны бенчмарки. Чтобы запустить их у себя, выполните команды:

Результаты бенчмарков можно увидеть на графиках (repeats / ms). Чем больше время выполнения, тем хуже результат.

При чтении самыми быстрыми оказались нативные структуры данных и Seamless-immutable.

Как сделать класс immutable

При записи самым быстрым оказался Mori. Seamless-immutable показал наихудший результат.

Как сделать класс immutable

Заключение

Эта статья будет полезна JavaScript-разработчикам, столкнувшимся с необходимостью использовать иммутабельные данные в своих приложениях для повышения производительности. В частности, это касается frontend-разработчиков, которые работают с фреймворками, использующими VirtualDOM (React, Mithril, Riot), а также Flux/Redux решения.

Источник

Ещё раз об ImmutableList в Java

В своей предыдущей статье «Рукоблудие вокруг ImmutableList в Java» я предложил вариант решения поднятой в статье «Неизменяемых коллекций в Java не будет – ни сейчас, ни когда-либо» проблемы отсутствия в Java неизменяемых списков.

Решение тогда было проработано только на уровне «есть такая идея», а реализация в коде была кривовата, поэтому и воспринято всё было несколько скептически. В данной статье предлагаю доработанный вариант решения. Логика использования и API доведены до приемлемого уровня. Реализация в коде – до уровня бета-версии.

Постановка задачи

Будем использовать определения из исходной статьи. В частности, это означает, что ImmutableList представляет собой неизменяемый список ссылок на какие-то объекты. Если эти объекты окажутся не immutable, то и список тоже не будет являться immutable объектом, несмотря на название. На практике это вряд ли кому-то помешает, но во избежание неоправданных ожиданий упомянуть надо.

Также понятно, что неизменяемость списка может быть «хакнута» посредством reflections, или создания своих классов в том же package с последующим залезанием в protected поля списка, или ещё чего-то подобного.

В отличие от исходной статьи, не будем придерживаться принципа «всё или ничего»: там автор, похоже, считает, что если проблема не может быть решена на уровне JDK, то и не стоит ничего делать. (На самом деле, ещё вопрос, «не может быть решена» или «у авторов Java не возникло желания её решить». Как мне кажется, всё-таки было бы возможно добавлением дополнительных интерфейсов, классов и методов привести существующие коллекции в более близкий к желаемому вид, хотя и менее красивый, чем если бы об этом задумались сразу. Но сейчас речь не об этом.)

Будем делать библиотеку, которая может успешно сосуществовать с имеющимися в Java коллекциями.

Основные идеи библиотеки:

ImmutableList

MutableList

Mutabor

Класс Mutabor предоставляет набор статических методов, являющихся «точками входа» в библиотеку.

Да, проект теперь называется «mutabor» (оно и созвучно с «mutable», и в переводе означает «я превращусь», что неплохо согласуется с идеей быстрых «превращений» одних типов коллекций в другие).

Методы copyTo* предназначены для создания соответствующих коллекций путём копирования предоставленных данных. Методы convertTo* предусматривают быстрое преобразование переданной коллекции в нужный тип, а если быстро преобразовать не удалось, то выполняют медленное копирование. Если быстрое преобразование прошло успешно, то исходная коллекция очищается, и предполагается, что в дальнейшем она не будет использоваться (хотя и может, но в этом вряд ли есть смысл).

Вызовы конструкторов объектов-реализаций ImmutableList / MutableList спрятаны. Предполагается, что пользователь имеет дело только с интерфейсами, сам такие объекты не создаёт, а для преобразования коллекций использует описанные выше методы.

Детали реализации

ImmutableListImpl

MutableListImpl

Метод snapshot выглядит так:

Реализация методов releaseSnapshot и contentEquals тривиальна.

Такой подход позволяет свести к минимуму количество копирований данных при «обыкновенном» использовании, заменив копирования на быстрые преобразования.

Быстрое преобразование списков

Быстрые преобразования возможны для классов ArrayList или Arrays$ArrayList (результат метода Arrays.asList() ). На практике в подавляющем большинстве случаев попадаются именно эти классы.

Внутри данные классы содержат массив элементов. Суть быстрого преобразования состоит в получении ссылки на этот массив через reflections (это private поле) и замене её ссылкой на пустой массив. Это гарантирует, что единственная ссылка на массив останется у нашего объекта, и массив останется неизменным.

В прошлой версии библиотеки быстрые преобразования типов коллекций осуществлялись вызовом конструктора. При этом исходный объект коллекции портился (становился непригодным для дальнейшего использования), чего от конструктора подсознательно не ожидаешь. Теперь для преобразования используется специальный статический метод, а исходная коллекция не портится, а просто очищается. Таким образом, пугающее необычное поведение было устранено.

Проблемы с equals / hashCode

Итого

На случай, если кто-то захочет использовать библиотеку, завёл группу в контактике для «обратной связи».

Использование библиотеки довольно очевидно, вот короткий пример:

Всем удачи! Шлите багрепорты!

Ой, у вас баннер убежал!

Читают сейчас

Редакторский дайджест

Присылаем лучшие статьи раз в месяц

Скоро на этот адрес придет письмо. Подтвердите подписку, если всё в силе.

Как сделать класс immutable

Похожие публикации

Теоретические структуры данных и их применение в JavaScript. Ч1. Пары

Рукоблудие вокруг ImmutableList в Java

Неизменяемых коллекций в Java не будет – ни сейчас, ни когда-либо

Курсы

AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Минуточку внимания

Комментарии 25

Как сделать класс immutable

Как сделать класс immutable

Как сделать класс immutable

Метод toList обеспечивает возможность передачи ImmutableList в куски кода, ожидающие List. Возвращается обёртка, в которой все изменяющие методы возвращают UnsupportedOperationException, а остальные методы переадресуются к исходному ImmutableList.

Вам не кажется, что с таким подходом нарушается принцип подстановки Лисков?

Метод contentEquals предназначен для сравнения содержимого списка с содержимым произвольного переданного Iterable (разумеется, осмысленной эта операция является только для тех реализаций Iterable, у которых есть какой-то внятный порядок элементов).

Как сделать класс immutable

Вам не кажется, что с таким подходом нарушается принцип подстановки Лисков?

Может стоит явно указать ограничение это в интерфейсе?

Как сделать класс immutable

Нет. Например, в контракте метода List.add явно прописано: throws UnsupportedOperationException if the add operation is not supported by this list.

Может стоит явно указать ограничение это в интерфейсе?

Тот случай, когда это ограничение очевидно.

Как сделать класс immutable

Меня восхищает Ваша напористость и желание сделать SDK лучше.
Сразу скажу, что я согласен с автором поста Неизменяемых коллекций в Java не будет – ни сейчас, ни когда-либо
И это согласие есть результат статьи, которую я практически написал, но в последний момент отказался публиковать — так как изменил свое мнение на мнение автора 🙂
Вот совсем кратко мои рассуждения на тему списков:

Чтобы ответить на этот вопрос, сначала нужно поискать уже существующие решения в самом SDK, а во-вторых, посмотреть на потенциальное решение с практической точки зрения. Начнем с поиска.
Классическим представителем неизменяемых объектов в SDK являются экземпляры класса java.lang.String. Строки спроектированы таким образом, что поменять их через публичное API — невозможно. Строки имеют отношение к поиску истины и потому, что их можно рассматривать через призму коллекций. Фактически, строка — это набор символов. Но сейчас вернемся к самому типу String. Важно понять, что в SDK нет MutableString. Или перефразировав, прийти к тому, что нет такой полной иерархии как ImmutableString, UnmodifiableString и MutableString. Плохо это или хорошо — каждый решает сам.
Что касается иерархии, то я немного слукавил — иерархия есть, только не для типа String, как чего-то целого и завершенного, а для набора символов. Это всем известные StringBuilder и StringBuffer, которые как и String растут от CharSequence. Фактически, у нас есть 2 ветки: изменяемые и неизменяемые последовательности символов. Причем во главу угла поставлен контракт на чтение данных, так как CharSequence содержит только методы получения данных.
После ознакомления с готовым решением по строкам в SDK можно сделать несколько важных выводов про mutable и immutable:

1) Каждая реализация сохраняет свое свойство навсегда — нет перехода от immutable к mutable или обратно внутри одной реализации.
2) В любой части программы, работая с реализацией, мы можем точно сказать о ее свойствах — изменяемая (StringBuilder/StringBuffer) или неизменяемая (String).
3) В любой части программы, работая с абстракцией (CharSequence), мы не можем сказать, какими свойствами данное представление обладает и какие гарантии может обеспечить (в общем случае).
4) Логичным и подтвержденным реализацией подходом является переход от mutable к immutable, т.е. изменяемый набор порождает неизменяемый, но не наоборот.

Выводы как постулаты мы будем использовать для реализации неизменяемых списков. Но сначала, я хотел бы проанализировать сами выводы и понять их сильные и слабые стороны. Мой любимый — это пункт 3. На мой взгляд, для проектирования безопасного api — это огромный недостаток. Когда мы спускаем в engine некоторую абстракцию, вполне логичным, для меня, является то, что этот engine имеет право потребовать определенные гарантии надежности от источника, взамен, обещает сделать свою работу должным образом. Такие отношения можно формализовать с помощью простого метода, спросив с помощью него, какие гарантии несет реализация. В плоскости рассматриваемой проблемы им может стать контрактный метод isImmutable;

Если мы используем фундаментальные принципы проектирования, то делать такой метод — неправильно. Но если мы ищем больше практичное решение, нежели фундаментальное — то допустимо.
Так как пункт 1 гласит, что свойство immutable/mutable сохраняется навсегда, то вмести с ним сохраняется и гарантия неизменяемости/изменяемости тоже — навсегда.
Рассматривая пункт 3, мы столкнулись с первой дилеммой — фундаментальность или практичность?
Пункт 2, можно интерпретировать и по-другому, что не должна одна реализация рости от второй. Т.е. ссылочное присвоение в обе стороны запрещены (ошибка компиляции).
Пункт 4, опять таки заставляет нас разобраться с дилеммой: фундаментальность или практичность? Потому что требует, наличие метода toImmutable в общем контракте. Можно читать toImmutable здесь как метод toString в классе CharSequence, с одной оговоркой.

Я утверждаю, что пункт 4 больше рожден из практической плоскости, нежели фундаментальной. И это важно. В противном случае, неизменяемая реализация должна порождать изменяемую, но на примере String, мы видим, что такого нет.
Есть еще одна особенность в типах String, StringBuilder и StringBuffer, если рассматривать их как набор символов, про которую я нарочно умолчал. Мы всегда работаем с реализацией, что сильно упрощает контекст восприятия.
Это недопустимо для списков в общем случае.
Если подытожить выше изложенную философию мысли, для решения проблемы с неизменяемыми коллекциями нам требуются в арсенале 2 метода isImmutable и toImmutable. Разумеется, исходим из того, что по факту сейчас есть в SDK.

Правильно? Не совсем. Потому что toImmutable возвращает контракт совместимый по апи с коллекциями в оба направления, что делает не возможным ограничение по типу.

И так далее, пока я не пришел к выводу, что новых коллекций не появится.

Если попытаться выдать желаемое за действительное — то в SDK все же неизменяемые коллекции могут быть и мне точно известно время — когда в java появится система типов, подобная TypeScript. Тогда различия между 2мя коллекциями будет в наличие значение у поля mutable: или true или false;
Но это совсем другая история, не находите?

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *