Я пытаюсь разобраться с изменчивыми и неизменными объектами. Использование изменяемых объектов вызывает много проблем (например, возвращает массив строк из метода), но у меня возникают проблемы с пониманием того, как это негативно влияет. Каковы лучшие практики использования изменяемых объектов? Вы должны избегать их, когда это возможно?
oop
immutability
mutable
Алекс Ангас
источник
источник
string
является неизменным, по крайней мере, в .NET, и я думаю, во многих других современных языках.Ответы:
Ну, есть несколько аспектов этого.
Изменяемые объекты без ссылочной идентичности могут вызывать ошибки в нечетные моменты времени. Например, рассмотрим
Person
компонент сequals
методом на основе значений :Person
Экземпляр получает «потерял» в карте , когда используется в качестве ключа , потому что егоhashCode
и равенство были основаны на изменяемых значений. Эти значения изменились за пределами карты, и все хеширование устарело. Теоретики любят говорить об этом, но на практике я не нашел, чтобы это было слишком большой проблемой.Другим аспектом является логическая «разумность» вашего кода. Это сложный термин для определения, охватывающий все от читаемости до потока. В общем, вы должны быть в состоянии посмотреть на кусок кода и легко понять, что он делает. Но что более важно, вы должны быть в состоянии убедить себя, что он делает то, что делает правильно . Когда объекты могут независимо изменяться в разных «доменах» кода, иногда становится трудно отследить, что и где (« пугающее действие на расстоянии »). Это более сложная концепция, но она часто встречается в больших и более сложных архитектурах.
Наконец, изменяемые объекты являются убийцами в параллельных ситуациях. Всякий раз, когда вы обращаетесь к изменчивому объекту из отдельных потоков, вам приходится иметь дело с блокировкой. Это снижает пропускную способность и делает ваш код значительно труднее поддерживать. Достаточно сложная система делает эту проблему настолько непропорциональной, что ее практически невозможно поддерживать (даже для экспертов по параллелизму).
Неизменяемые объекты (и, в частности, неизменяемые коллекции) позволяют избежать всех этих проблем. Как только вы разберетесь с тем, как они работают, ваш код превратится в нечто, что будет легче читать, легче поддерживать и с меньшей вероятностью не получится непредсказуемым и непредсказуемым образом. Неизменяемые объекты еще проще тестировать не только из-за их легкого использования, но и из-за шаблонов кода, которые они стремятся применять. Короче говоря, они хорошая практика во всем!
С учетом сказанного, я вряд ли фанат в этом вопросе. Некоторые проблемы просто плохо моделируются, когда все неизменно. Но я думаю, что вы должны попытаться подтолкнуть как можно больше своего кода в этом направлении, при условии, конечно, что вы используете язык, который делает это разумным мнением (C / C ++ делает это очень трудным, как и Java) , Короче говоря: преимущества в некоторой степени зависят от вашей проблемы, но я бы предпочел неизменность.
источник
Неизменяемые предметы против неизменных коллекций
Одним из лучших моментов в дискуссии о изменчивых и неизменных объектах является возможность распространения концепции неизменности на коллекции. Неизменяемый объект - это объект, который часто представляет единую логическую структуру данных (например, неизменяемая строка). Когда у вас есть ссылка на неизменный объект, содержимое объекта не изменится.
Неизменная коллекция - это коллекция, которая никогда не меняется.
Когда я выполняю операцию с изменяемой коллекцией, я изменяю коллекцию на месте, и все сущности, которые имеют ссылки на коллекцию, увидят это изменение.
Когда я выполняю операцию с неизменяемой коллекцией, в новую коллекцию возвращается ссылка, отражающая это изменение. Все объекты, которые имеют ссылки на предыдущие версии коллекции, не увидят изменения.
Умные реализации не обязательно должны копировать (клонировать) всю коллекцию, чтобы обеспечить эту неизменность. Простейшим примером является стек, реализованный в виде односвязного списка, и операции push / pop. Вы можете повторно использовать все узлы из предыдущей коллекции в новой коллекции, добавив только один узел для push и не клонировав ни одного узла для pop. Операция push_tail в односвязном списке, с другой стороны, не так проста и эффективна.
Неизменяемые и изменчивые переменные / ссылки
Некоторые функциональные языки применяют концепцию неизменности к самим ссылкам на объекты, допуская только одно присвоение ссылок.
Простота разработки и производительность
Почти всегда причина использования неизменяемого объекта заключается в содействии программированию без побочных эффектов и простым рассуждениям о коде (особенно в среде с высокой степенью параллелизма / параллельности). Вам не нужно беспокоиться об изменении базовых данных другим объектом, если объект неизменен.
Основным недостатком является производительность. Вот описание простого теста, который я провел на Java, сравнивая некоторые неизменяемые и изменяемые объекты в игрушечной задаче.
Проблемы с производительностью спорны во многих приложениях, но не во всех, поэтому многие большие числовые пакеты, такие как класс Numpy Array в Python, допускают обновления больших массивов на месте. Это было бы важно для областей применения, которые используют большие матричные и векторные операции. Эти большие параллельные с данными и вычислительно сложные задачи достигают большого ускорения при работе на месте.
источник
Проверьте это сообщение в блоге: http://www.yegor256.com/2014/06/09/objects-should-be-immutable.html . Это объясняет, почему неизменяемые объекты лучше, чем изменяемые. Коротко:
источник
Неизменяемые объекты - очень мощная концепция. Они снимают с себя много бремени, пытаясь поддерживать согласованность объектов / переменных для всех клиентов.
Вы можете использовать их для низкоуровневых, неполиморфных объектов, таких как класс CPoint, которые в основном используются с семантикой значений.
Или вы можете использовать их для полиморфных интерфейсов высокого уровня - как IFunction, представляющая математическую функцию - которая используется исключительно с семантикой объекта.
Самое большое преимущество: неизменность + семантика объекта + умные указатели делают владение объектом несущественной, все клиенты объекта по умолчанию имеют свою собственную личную копию. Неявно это также означает детерминированное поведение при наличии параллелизма.
Недостаток: при использовании с объектами, содержащими много данных, потребление памяти может стать проблемой. Решением этой проблемы может быть сохранение операций над объектом символическими и выполнение ленивых вычислений. Однако это может привести к цепочке символьных вычислений, что может отрицательно повлиять на производительность, если интерфейс не предназначен для размещения символьных операций. В этом случае нужно определенно избегать возврата огромных кусков памяти из метода. В сочетании с цепными символьными операциями это может привести к значительному потреблению памяти и снижению производительности.
Таким образом, неизменяемые объекты, безусловно, являются моим основным способом мышления об объектно-ориентированном дизайне, но они не являются догмой. Они решают много проблем для клиентов объектов, но и создают много, особенно для разработчиков.
источник
Вы должны указать, на каком языке вы говорите. Для языков низкого уровня, таких как C или C ++, я предпочитаю использовать изменяемые объекты для экономии места и уменьшения оттока памяти. В языках более высокого уровня неизменяемые объекты облегчают анализ поведения кода (особенно многопоточного кода), потому что нет «жуткого действия на расстоянии».
источник
Изменяемый объект - это просто объект, который можно изменить после того, как он создан / создан, в отличие от неизменяемого объекта, который нельзя изменить (см. Страницу Википедии по теме). Примером этого в языке программирования являются списки и кортежи Pythons. Списки могут быть изменены (например, новые элементы могут быть добавлены после его создания), тогда как кортежи не могут.
Я не думаю, что есть четкий ответ, какой из них лучше для всех ситуаций. У них обоих есть свои места.
источник
Если тип класса является изменчивым, переменная этого типа может иметь несколько различных значений. Например, предположим, что у объекта
foo
есть полеint[] arr
, и оно содержит ссылкуint[3]
на число {5, 7, 9}. Несмотря на то, что тип поля известен, оно может представлять как минимум четыре разные вещи:Потенциально разделяемая ссылка, все держатели которой заботятся только о том, чтобы она инкапсулировала значения 5, 7 и 9. Если она
foo
хочетarr
инкапсулировать разные значения, она должна заменить ее другим массивом, который содержит требуемые значения. Если кто-то хочет сделать копиюfoo
, он может дать копии либо ссылку,arr
либо новый массив, содержащий значения {1,2,3}, в зависимости от того, что удобнее.Единственная ссылка где-либо в юниверсе на массив, который инкапсулирует значения 5, 7 и 9. набор из трех хранилищ, которые в настоящий момент содержат значения 5, 7 и 9; Если
foo
он хочет, чтобы он инкапсулировал значения 5, 8 и 9, он может либо изменить второй элемент в этом массиве, либо создать новый массив, содержащий значения 5, 8 и 9, и отказаться от старого. Обратите внимание, что если кто-то хотел сделать копиюfoo
, он должен в копии заменитьarr
ссылку на новый массив,foo.arr
чтобы оставаться единственной ссылкой на этот массив где-либо в юниверсе.Ссылка на массив, который принадлежит некоторому другому объекту, который по
foo
какой-то причине подверг его воздействию (например, возможно, он хочетfoo
сохранить там некоторые данные). В этом сценарииarr
инкапсулируется не содержимое массива, а его идентификатор . Поскольку заменаarr
ссылкой на новый массив полностью изменит его значение, копияfoo
должна содержать ссылку на тот же массив.Ссылка на массив,
foo
единственным владельцем которого является, но по какой-то причине ссылки на него хранятся другим объектом (например, он хочет иметь другой объект для хранения данных - обратная сторона предыдущего случая). В этом сценарииarr
инкапсулируется как идентификатор массива, так и его содержимое. Заменаarr
ссылкой на новый массив полностью изменит его значение, но наличиеarr
ссылки на клонfoo.arr
нарушит предположение, чтоfoo
это единственный владелец. Таким образом, нет возможности скопироватьfoo
.В теории
int[]
должен быть хороший простой четко определенный тип, но он имеет четыре совершенно разных значения. Напротив, ссылка на неизменный объект (напримерString
) обычно имеет только одно значение. Большая часть «силы» неизменных объектов проистекает из этого факта.источник
Изменчивые экземпляры передаются по ссылке.
Неизменные экземпляры передаются по значению.
Абстрактный пример. Предположим, что на моем жестком диске существует файл с именем txtfile . Теперь, когда вы спрашиваете у меня txtfile , я могу вернуть его в двух режимах:
В первом режиме возвращаемый txtfile является изменяемым файлом, потому что когда вы вносите изменения в файл ярлыка, вы вносите изменения и в исходный файл. Преимущество этого режима заключается в том, что для каждого возвращаемого ярлыка требуется меньше памяти (в оперативной памяти или на жестком диске), а недостатком является то, что каждый (не только я, владелец) имеет права на изменение содержимого файла.
Во втором режиме возвращаемый txtfile является неизменяемым файлом, поскольку все изменения в полученном файле не относятся к исходному файлу. Преимущество этого режима заключается в том, что только я (владелец) может изменять исходный файл, а недостатком является то, что для каждой возвращаемой копии требуется память (в ОЗУ или на жестком диске).
источник
Если вы возвращаете ссылки на массив или строку, то внешний мир может изменить содержимое этого объекта и, следовательно, сделать его изменяемым (модифицируемым) объектом.
источник
Неизменяемые означает, что нельзя изменить, а изменяемые означает, что вы можете изменить.
Объекты отличаются от примитивов в Java. Примитивы встроены в типы (логические, int и т. Д.), А объекты (классы) - это созданные пользователем типы.
Примитивы и объекты могут быть изменяемыми или неизменными, если они определены как переменные-члены в реализации класса.
Многие люди думают, что примитивы и переменные объекта, имеющие перед ними конечный модификатор, неизменны, однако это не совсем так. Так что final почти не означает неизменяемость для переменных. Смотрите пример здесь
http://www.siteconsortium.com/h/D0000F.php .
источник
Immutable object
- это состояние объекта, которое нельзя изменить после создания. Объект неизменен, когда все его поля неизменныПоток безопасно
Основным преимуществом неизменяемого объекта является то, что он является естественным для параллельной среды. Самая большая проблема в параллелизме - это то,
shared resource
что может быть изменено любым потоком. Но если объект является неизменным, тоread-only
это работа с потоками. Любая модификация исходного неизменяемого объекта возвращает копиюпобочные эффекты бесплатно
Как разработчик, вы абсолютно уверены, что состояние неизменяемого объекта не может быть изменено из любого места (намеренно или нет)
оптимизация компиляции
Повысить производительность
Недостаток:
Копирование объекта - более сложная операция, чем изменение изменяемого объекта, поэтому он имеет некоторое влияние на производительность
Для создания
immutable
объекта вы должны использовать:Уровень языка. Каждый язык содержит инструменты, которые помогут вам в этом. Например, Java имеет
final
иprimitives
Swift имеетlet
иstruct
[О] . Язык определяет тип переменной. Например, Java имеетprimitive
иreference
тип, Swift имеетvalue
иreference
тип [О] . Для неизменяемых объектов более удобным являетсяprimitives
иvalue
тип, который делает копию по умолчанию. Что касаетсяreference
типа, это более сложно (потому что вы можете изменить состояние объекта из него), но возможно. Например, вы можете использоватьclone
шаблон на уровне разработчикаУровень разработчика. Как разработчик, вы не должны предоставлять интерфейс для изменения состояния
источник