Подтвердить что ты не робот

Что такое неизменность и почему я должен беспокоиться об этом?

Я прочитал пару статей о неизменности, но не очень хорошо придерживаюсь концепции.

Недавно я сделал поток, в котором упоминалась неизменность, но поскольку это тема сама по себе, я делаю выделенный поток сейчас.

Я упомянул в прошлом потоке, что я думал, что неизменность - это процесс создания объекта только для чтения и обеспечения его низкой видимости. Другой член сказал, что на самом деле это не имеет никакого отношения. Эта страница (часть серия) использует пример неизменяемый класс /struct, и он использует readonly и другие понятия, чтобы заблокировать его.

Что такое определение состояния в случае этого примера? Состояние - это понятие, которое я действительно не понял.

С точки зрения дизайна, неизменяемый класс должен быть тем, который не принимает пользовательский ввод и действительно просто возвращает значения?

Я понимаю, что любой объект, который только возвращает информацию, должен быть неизменным и "заблокирован", верно? Поэтому, если я хочу вернуть текущее время в выделенном классе с помощью одного метода, я должен использовать ссылочный тип, так как это будет работать ссылкой типа и, следовательно, я получаю пользу от неизменности.

4b9b3361

Ответ 1

Что такое неизменность?

  • Неизменяемость применяется прежде всего к объектам (строкам, массивам, пользовательскому классу животных).
  • Как правило, если существует неизменяемая версия класса, доступна также изменяемая версия. Например, Objective-C и Cocoa определяют как класс NSString (неизменяемый), так и класс NSMutableString.
  • Если объект неизменен, он не может быть изменен после его создания (в основном для чтения). Вы могли бы подумать об этом как о "только конструктор может изменить объект".

Это не имеет прямого отношения к пользовательскому вводу; даже ваш код не может изменить значение неизменяемого объекта. Однако вы всегда можете создать новый неизменяемый объект для его замены. Здесь пример псевдокода; обратите внимание, что на многих языках вы можете просто сделать myString = "hello"; вместо использования конструктора, как я сделал ниже, но я включил его для ясности:

String myString = new ImmutableString("hello");
myString.appendString(" world"); // Can't do this
myString.setValue("hello world"); // Can't do this
myString = new ImmutableString("hello world"); // OK

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

Преимущества иммунитета

  • Если вы передаете объект другой функции/методу, вам не придется беспокоиться о том, будет ли этот объект иметь такое же значение после возвращения функции. Например:

    String myString = "HeLLo WoRLd";
    String lowercasedString = lowercase(myString);
    print myString + " was converted to " + lowercasedString;
    

    Что делать, если реализация lowercase() изменила myString, поскольку она создавала версию в нижнем регистре? Третья строка не даст вам результат, который вы хотели. Конечно, хорошая функция lowercase() не сделала бы этого, но вам гарантирован этот факт, если myString неизменен. Таким образом, неизменяемые объекты могут помочь обеспечить хорошие объектно-ориентированные методы программирования.

  • Легче сделать безопасный объект без потолка

  • Это потенциально упрощает реализацию класса (хорошо, если вы тот, кто пишет класс)

State

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

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

Ответ 2

Неизменность

Проще говоря, память неизменна, когда она не изменяется после инициализации.

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

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

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

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

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

Многие языки функционального программирования, такие как Lisp, Haskell, Erlang, F # и Clojure, поощряют неизменные структуры данных по самой своей природе. Именно по этой причине они наслаждаются возрождением интереса, когда мы движемся к все более сложной многопоточной разработке приложений и компьютерным архитектурам с множеством компьютеров.

Государственные

Состояние приложения можно просто рассматривать как содержимое всех регистров памяти и процессора в данный момент времени.

Логически, состояние программы можно разделить на две части:

  • Состояние кучи
  • Состояние стека каждого исполняемого потока

В управляемых средах, таких как С# и Java, один поток не может получить доступ к памяти другого. Поэтому каждый поток "владеет" состоянием своего стека. Стек можно рассматривать как локальные переменные и параметры типа значения (struct) и ссылки на объекты. Эти значения изолированы от внешних потоков.

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

В ООП состояние экземпляра класса определяется его полями. Эти поля хранятся в куче и поэтому доступны из всех потоков. Если класс определяет методы, которые позволяют изменять поля после завершения конструктора, класс изменен (не является неизменным). Если поля не могут быть изменены каким-либо образом, то тип неизменен. Важно отметить, что класс с изменчивым полем не обязательно непреложный. Например, в С# только потому, что поле типа List<object> определяется как readonly, фактическое содержимое списка может быть изменено в любое время.

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

На практике может быть неудобно определять все ваши типы как неизменные. Чтобы изменить значение на неизменяемый тип, может потребоваться справедливое копирование памяти. Некоторые языки упрощают этот процесс, чем другие, но в любом случае ЦП в конечном итоге сделает дополнительную работу. Многие факторы способствуют определению того, превышает ли время, затрачиваемое на копирование памяти, влияние блокирующих утверждений.

Много исследований было посвящено разработке неизменных структур данных, таких как списки и деревья. При использовании таких структур, скажем, список, операция "добавить" вернет ссылку на новый список с добавленным новым элементом. Ссылки на предыдущий список не видят изменений и по-прежнему имеют согласованное представление данных.

Ответ 3

Проще говоря: после создания неизменяемого объекта нет способа изменить содержимое этого объекта. Примерами неизменяемых объектов .Net являются String и Uri.

Когда вы изменяете строку, вы просто получаете новую строку. Исходная строка не изменится. Ури имеет только свойства readonly и нет доступных методов для изменения содержимого Uri.

Случаи важности неизменяемых объектов различны и в большинстве случаев связаны с безопасностью. Ури - хороший пример. (например, вы не хотите, чтобы Uri был изменен некоторым ненадежным кодом.) Это означает, что вы можете передать ссылку на неизменяемый объект, не беспокоясь о том, что содержимое когда-либо изменится.

Надеюсь, это поможет.

Ответ 4

Вещи, которые неизменны, никогда не меняются. Изменчивые вещи могут измениться. Мутируемые вещи мутируют. Неизменяемые вещи, похоже, меняются, но на самом деле создают новую изменчивую вещь.

Например, вот карта в Clojure

(def imap {1 "1" 2 "2"})
(conj imap [3 "3"])
(println imap)

Первая строка создает новую неизменяемую карту Clojure. Вторая строка соединяет 3 и "3" с картой. Это может выглядеть так, как если бы оно модифицировало старую карту, но на самом деле она возвращает новую карту с добавлением 3 "3" . Это яркий пример непреложности. Если бы это была измененная карта, она просто добавила бы 3 "3" непосредственно к той же старой карте. Третья строка печатает карту

{3 "3", 1 "1", 2 "2"}

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

Ответ 5

Хороший вопрос.

Многопоточность. Если все типы являются неизменными, условия гонки не существуют, и вы можете выбросить столько кодов, сколько хотите.

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

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

Также неизменность уменьшает количество возможностей в программе, что снижает потенциальную сложность и вероятность ошибок.

Ответ 6

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

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

Ответ 7

Позвольте мне добавить еще одну вещь. Помимо всего, что было упомянуто выше, вы также хотите неизменность для:

  • объекты значений (иногда также называемые объектами данных или pojo)
  • structs (в С#/. NET) - см. fooobar.com/questions/101662/...

Ответ 8

Создание непреложных вещей предотвращает большое количество распространенных ошибок.

Например, у Студента никогда не должно быть изменений на их ученике #. Если вы не предоставляете способ установить переменную (и сделать ее const, или final, или независимо от того, что поддерживает ваш язык), вы можете принудительно выполнить это во время компиляции.

Если вещи изменяемы, и вы не хотите, чтобы они менялись, когда вы их проходили, вам нужно сделать обходную копию, которую вы передадите. Затем, если вызываемый метод/функция изменяет копию элемента, нетронутый оригинал.

Приведение вещей в неизменное означает, что вам не нужно запоминать (или брать время/память) для создания защищенных копий.

Если вы действительно работаете над этим, и подумайте о каждой переменной, которую вы делаете, вы обнаружите, что подавляющее большинство (обычно у меня 90-95%) ваших переменных никогда не меняются, как только они получают значение. Это упрощает выполнение программ и уменьшает количество ошибок.

Чтобы ответить на ваш вопрос о состоянии, состояние - это значения, которые имеют переменные "объекта" (будь то класс или структура). Если вы приняли состояние человека "объект", это будут такие вещи, как цвет глаз, цвет волос, длина волос и т.д.... некоторые из них (например, цвет глаз) не меняются, в то время как другие, такие как длина волос, меняются.

Ответ 9

"... почему я должен беспокоиться об этом?"

Практическим примером является повторяющаяся конкатенация строк. Например, в .NET:

string SlowStringAppend(string [] files)
{
    // Declare an string
    string result="";

    for (int i=0;i<files.length;i++)
    {
        // result is a completely new string equal to itself plus the content of the new
        // file
        result = result + File.ReadAllText(files[i]);
    }

    return result;
}    

string EfficientStringAppend(string [] files)
{
    // Stringbuilder manages a internal data buffer that will only be expanded when absolutely necessary
    StringBuilder result=new SringBuilder();

    for (int i=0;i<files.length;i++)
    {
        // The pre-allocated buffer (result) is appended to with the new string 
        // and only expands when necessary.  It doubles in size each expansion
        // so need for allocations become less common as it grows in size. 
        result.Append(File.ReadAllText(files[i]));
    }

    return result.ToString();
}

К сожалению, использование первого (медленного) функционального подхода все еще широко используется. Понимание неизменности дает понять, почему использование StringBuilder настолько важно.

Ответ 10

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

Обратите внимание, что при этом мы теперь используем дополнительную память. Некоторые для старого значения, некоторые для нового значения. Также обратите внимание, некоторые люди путаются, потому что они смотрят на код, например:

string mystring = "inital value";
mystring = "new value";
System.Console.WriteLine(mystring); // Outputs "new value";

и подумайте о себе ", но я меняю его, смотрю прямо там, в черно-белом! mystring выводит" новое значение "...... Я думал, вы сказали, что я не могу его изменить?!!"

Но на самом деле под капотом, что происходит, это выделение новой памяти, то есть mystring теперь указывает на другой адрес и пространство памяти. "Неизменяемый" в этом смысле не относится к значению mystring, а скорее к памяти, используемой переменной mystring для хранения ее значения.

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

Одно из мест, которое это действительно взрывает: использование памяти в сильно повторяющихся циклах, особенно со строками, как в записи Ashs. Предположим, вы строили HTML-страницу в итеративном цикле, где вы постоянно добавляли следующий блок HTML до последнего, и только для ударов вы делали это на сервере с большими объемами. Это постоянное распределение "новой памяти значений" может быстро стать дорогостоящим и, в конечном счете, фатальным, если "старая память значений" неправильно очищается.

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

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

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

Как и в публикации Ashs, в .Net и со строками, рекомендуется использовать изменчивый класс StringBuilder, а не неизменные классы строк, когда дело доходит до необходимости постоянно изменять значение строк.

Другие языки/типы также будут иметь свои собственные методы обхода.

Ответ 11

Почему неизменность?

  • Они менее подвержены ошибкам и более безопасны.

  • Неизменяемые классы легче разрабатывать, реализовывать и использовать, чем изменяемые классы.

  • Неизменяемые объекты потокобезопасны, поэтому проблем с синхронизацией нет.

  • Неизменяемые объекты являются хорошими клавишами Карты и Устанавливают элементы, поскольку они обычно не изменяются после создания.

  • Неизбежность упрощает запись, использование и рассуждение о коде (инвариант класса устанавливается один раз, а затем не изменяется).

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

  • Внутреннее состояние программы будет согласованным, даже если у вас есть исключения.

  • Ссылки на неизменяемые объекты могут быть кэшированы, поскольку они не будут меняться. (т.е. в Hashing он обеспечивает быструю операцию).

Смотрите мой блог для получения более подробного ответа:
http://javaexplorer03.blogspot.in/2015/07/minimize-mutability.html

Ответ 12

Посмотрите, я не читал ссылки, которые вы опубликовали.

Однако, вот мое понимание.
Каждая программа имеет некоторые знания о ней данных (состоянии), которые могут меняться либо путем ввода/внешних изменений и т.д.

Переменные (значения, которые изменяются) сохраняются для поддержания состояния. Неизменяемость означает некоторые данные, которые не изменяются. Вы можете сказать, что он такой же, как readonly или constant, каким-то образом (это можно увидеть так).

AFAIK, функциональное программирование имеет непреложные вещи (т.е. вы не можете использовать назначение переменной, удерживающей это значение. Что вы можете сделать, это создать другую переменную, которая может содержать исходное значение + изменения).

.net имеет класс string, который является примером.
т.е. вы не можете изменить строку на своем месте

string s = "hello"; Я могу написать s.Replace( "el", "a" ); Но это не изменит содержимое переменной s.

Что я могу сделать, это s = s.Replace( "el", "a" );
Это создаст новую переменную и присвоит ее значение s (переписывая содержимое s).

Эксперты могут исправить ошибки, если у меня есть, в моем понимании.

EDIT: Immutable = Unassignable, когда он держит некоторое значение и не может быть заменен на месте (возможно?)

Ответ 13

Пример потенциальных преимуществ производительности, предоставляемых неизменяемыми объектами, доступен в API WPF. Общим базовым классом многих типов WPF является Freezable.

Несколько примеров WPF показывают, что замораживание объектов (что делает их неизменными во время выполнения) может значительно повысить производительность приложения, поскольку блокировка и копирование не требуются.

Лично мне хотелось бы, чтобы понятие неизменности было легче выразить на языке, который я использую чаще всего, С#. Для полей доступен модификатор readonly. Я хотел бы видеть модификатор readonly для типов, который будет разрешен только для типов, которые имеют только поля только для чтения, которые имеют только типы readonly. По сути, это означает, что все состояние нужно будет вводить во время построения, и этот и весь графический объект будет заморожен. Я полагаю, что это были метаданные, присущие CLR, тогда их можно было бы легко использовать для оптимизации анализа мусора для GC.

Ответ 14

Извините, почему неизменяемость препятствует условиям гонки (в этом примере пишите после ошибок чтения)?

shared v = Integer(3)
v = Integer(v.value() + 1) # in parallel