Если неизменяемые объекты хороши, почему люди продолжают создавать изменяемые объекты? [закрыто]

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

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


¹ Неизменяемый объект - это объект, состояние которого не может быть изменено после его создания.
²² Mutable object - это объект, который может быть изменен после его создания.

250 голосов | спросил 6 revs, 5 users 38%
ahsteele
1 Jam1000000amThu, 01 Jan 1970 03:00:00 +030070 1970, 03:00:00

23 ответа


327

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

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

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

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

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

ответил Marius Balčytis 12 +03002016-10-12T00:59:59+03:00312016bEurope/MoscowWed, 12 Oct 2016 00:59:59 +0300 2016, 00:59:59
130

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

ответил Marius Balčytis 12 +03002016-10-12T00:59:59+03:00312016bEurope/MoscowWed, 12 Oct 2016 00:59:59 +0300 2016, 00:59:59
49
  1. Есть место для изменчивости. принципы, основанные на ведении бизнеса обеспечивают четкое понимание того, что должно быть изменчивым и что должно быть неизменным. Если вы подумаете об этом, вы поймете, что нецелесообразно представлять себе систему, в которой каждое изменение состояния к объекту требует разрушения и пересоздания его и каждого объекта, на который он ссылается. С помощью сложных систем это может привести к полному стиранию и восстановлению графа объектов всей системы.

  2. Большинство разработчиков ничего не делают, когда требования к производительности достаточно значительны, чтобы им нужно было сосредоточиться на параллелизме (или на многих других проблемах, которые общеизвестно, что хорошо осведомлены).

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

    открытый класс ImmutablePerson {
    
         public ImmutablePerson (имя строки, ImmutableEventList eventsToAttend)
         {
              this.name = name;
              this.eventsToAttend = eventsToAttend;
         }
         имя частной строки;
         частные события ImmutableEventListToAttend;
    
         public string Имя {get {return this.name; }}
    
         public ImmutablePerson RSVP (ImmutableEvent immutableEvent) {
             //человек является RSVPing событием, тем самым изменяя состояние
             //событийToAttend. поэтому нам нужен новый человек со ссылкой
             //к новому событию
             ImmutableEvent newEvent = immutableEvent.OnRSVPReceived (это);
             ImmutableEventList newEvents = this.eventsToAttend.Add (newEvent));
             var newSelf = new ImmutablePerson (name, newEvents);
             return newSelf;
         }
        }
    
        открытый класс ImmutableEvent {
         public ImmutableEvent (DateTime, когда, ImmutablePersonList peopleAttending, ImmutablePersonList peopleNotAttending) {
             this.when = when;
             this.peopleAttending = peopleAttending;
             this.peopleNotAttending = peopleNotAttending;
         }
         private DateTime, когда;
         частный ImmutablePersonList peopleAttending;
         частный ImmutablePersonList peopleNotAttending;
         public ImmutableEvent OnReschedule (DateTime when) {
               вернуть новый ImmutableEvent (когда, peopleAttending, peopleNotAttending);
         }
         //заметим, что это будет бесконечный цикл, потому что каждый раз один экземпляр
         //добавляется двунаправленное отношение, его содержащие изменения объекта
         //означает, что он должен перестроить другую версию
         //представляет мутированное состояние, другое должно обновить его
         //ссылается, тем самым перекрывая ссылку на первый объект на него и
         //необходимость рекурсии
         public ImmutableEvent OnRSVPReceived (ImmutablePerson immutablePerson) {
               if (this.peopleAttending.Contains (immutablePerson)) возвращает это;
               ImmutablePersonList посещает = this.peopleAttending.Add (immutablePerson);
               ImmutablePersonList notAttending = this.peopleNotAttending.Contains (immutablePerson)
                                    ? peopleNotAttending.Remove (immutablePerson)
                                    : peopleNotAttending;
               вернуть новый ImmutableEvent (когда, посещая, а не Attending);
         }
        }
        открытый класс ImmutablePersonList
        {
          private ImmutablePerson [] immutablePeople;
          public ImmutablePersonList (ImmutablePerson [] immutablePeople) {
              this.immutablePeople = immutablePeople;
          }
          public ImmutablePersonList Добавить (ImmutablePerson newPerson) {
              if (this.Contains (newPerson)) возвращает это;
              ImmutablePerson [] newPeople = new ImmutablePerson [immutablePeople.Length];
              for (var i = 0; i <immutablePeople.Length; i ++)
                  newPeople [i] = this.immutablePeople [i];
              newPeople [immutablePeople.Length] = newPerson;
          }
          public ImmutablePersonList Удалить (ImmutablePerson newPerson) {
              if (immutablePeople.IndexOf (newPerson)! = -1)
              ImmutablePerson [] newPeople = new ImmutablePerson [immutablePeople.Length-2];
              bool hasPassedRemoval = false;
              for (var i = 0; i <immutablePeople.Length; i ++)
              {
                 hasPassedRemoval = hasPassedRemoval || immutablePeople [i] == newPerson;
                 newPeople [i] = this.immutablePeople [hasPassedRemoval? i + 1: i];
              }
              вернуть новый ImmutablePersonList (newPeople);}
          public bool Содержит (ImmutablePerson immutablePerson) {
             return this.immutablePeople.IndexOf (immutablePerson)! = -1;
          }
        }
        открытый класс ImmutableEventList
        {
          частный ImmutableEvent [] immutableEvents;
          public ImmutableEventList (ImmutableEvent [] immutableEvents) {
              this.immutableEvents = immutableEvents;
          }
          public ImmutableEventList Добавить (ImmutableEvent newEvent) {
              if (this.Contains (newEvent)) возвращает это;
              ImmutableEvent [] newEvents = new ImmutableEvent [immutableEvents.Length];
              for (var i = 0; i <immutableEvents.Length; i ++)
                  newEvents [i] = this.immutableEvents [i];
              newEvents [immutableEvents.Length] = newEvent;
          }
          public ImmutableEventList Удалить (ImmutableEvent newEvent) {
              if (immutableEvents.IndexOf (newEvent)! = -1)
              ImmutableEvent [] newEvents = new ImmutableEvent [immutableEvents.Length-2];
              bool hasPassedRemoval = false;
              for (var i = 0; i <immutablePeople.Length; i ++)
              {
                 hasPassedRemoval = hasPassedRemoval || immutableEvents [i] == newEvent;
                 newEvents [i] = this.immutableEvents [hasPassedRemoval? i + 1: i];
              }
              вернуть новый ImmutableEventList (newPeople);
          }
          public bool Содержит (ImmutableEvent immutableEvent) {
             return this.immutableEvent.IndexOf (immutableEvent)! = -1;
          }
        }
    
ответил Marius Balčytis 12 +03002016-10-12T00:59:59+03:00312016bEurope/MoscowWed, 12 Oct 2016 00:59:59 +0300 2016, 00:59:59
36

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

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

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

ответил Marius Balčytis 12 +03002016-10-12T00:59:59+03:00312016bEurope/MoscowWed, 12 Oct 2016 00:59:59 +0300 2016, 00:59:59
29

С моей точки зрения, это недостаточная осведомленность. Если вы посмотрите на другие известные языки JVM (Scala, Clojure), изменяемые объекты редко встречаются в коде, и поэтому люди начинают использовать их в сценариях, где одиночной потоковой передачи недостаточно.

В настоящее время я изучаю Clojure и имею небольшой опыт работы в Scala (4 года + на Java), и ваш стиль кодирования меняется из-за осознания состояния.

ответил Marius Balčytis 12 +03002016-10-12T00:59:59+03:00312016bEurope/MoscowWed, 12 Oct 2016 00:59:59 +0300 2016, 00:59:59
13

Я думаю, что один важный фактор был проигнорирован: Java Beans в значительной степени полагается на определенный стиль мутирующих объектов, и (особенно учитывая источник) довольно многие люди воспринимают это как (или даже /em>) канонический пример использования all Java.

ответил Marius Balčytis 12 +03002016-10-12T00:59:59+03:00312016bEurope/MoscowWed, 12 Oct 2016 00:59:59 +0300 2016, 00:59:59
12

Каждая корпоративная система Java, над которой я работал в своей карьере, использует либо Hibernate, либо Java Persistence API (JPA). Hibernate и JPA по существу диктуют, что ваша система использует изменчивые объекты, потому что все их предположение заключается в том, что они обнаруживают и сохраняют изменения в ваших объектах данных. Для многих проектов простота разработки, которую предлагает Hibernate, является более привлекательной, чем преимущества неизменяемых объектов.

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

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

ответил Marius Balčytis 12 +03002016-10-12T00:59:59+03:00312016bEurope/MoscowWed, 12 Oct 2016 00:59:59 +0300 2016, 00:59:59
9

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

Многие программы предназначены для моделирования реальных вещей, которые по своей сути изменяемы. Предположим, что в 12:51 некоторые переменные AllTrucks содержат ссылку на объект # 451, который является корнем структуры данных, которая указывает, какой груз содержится во всех грузовиках флота в этот момент ( 12:51 утра), а некоторые переменные BobsTruck могут использоваться для получения ссылки на объект # 24601 указывает на объект, который указывает, какой груз содержится в грузовике Боба в этот момент (12:51 утра). В 12:52 некоторые грузовики (включая Боба) загружаются и выгружаются, а структуры данных обновляются, так что AllTrucks теперь будет содержать ссылку на структуру данных, которая указывает, что груз находится во всех грузовиках, как от 12:52.

Что должно произойти с BobsTruck?

Если свойство «груза» каждого объекта грузового автомобиля является неизменным, тогда объект № 24601 навсегда будет представлять состояние, которое грузовик Боба имел в 12:51 утра. Если BobsTruck содержит прямую ссылку на объект # 24601, то если код обновления, обновляющий AllTrucks, также не обновляется BobsTruck), он перестает представлять текущее состояние грузовика Боба. Обратите внимание, что если BobsTruck не хранится в какой-либо форме изменчивого объекта, единственный способ, которым может обновить код, который обновляет AllTrucks, будет, если код явно запрограммирован поэтому.

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

К сожалению, использование такой функции каждый раз, когда вы хотите получить доступ к состоянию грузовика Боба, может стать довольно раздражающим и громоздким. Альтернативный подход состоял бы в том, чтобы сказать, что объект # 24601 всегда и навсегда (пока кто-либо держит ссылку на него) представляет состояние current грузовика Боба. Код, который захочет повторно обращаться к текущему состоянию грузовика Боба, не должен был запускать какую-то трудоемкую функцию каждый раз - он мог бы просто выполнить функцию поиска один раз, чтобы узнать, что объект № 24601 является грузовиком Боба, а затем просто доступ к этому объекту в любое время, когда он хочет увидеть текущее состояние грузовика Боба.

Обратите внимание, что функциональный подход не лишен преимуществ в однопоточной среде или в многопоточной среде, где потоки будут в основном просто наблюдать данные, а не изменять их. Любой поток наблюдателя, который копирует ссылку на объект, содержащуюся в AllTrucks, а затем исследует состояния грузовика, представленные таким образом, будет видеть состояние всех грузовиков с момента, когда он схватил ссылку. Каждый раз, когда поток наблюдателя хочет видеть более новые данные, он может просто перехватить ссылку. С другой стороны, наличие всего состояния флота, представленного одним неизменным объектом, исключало бы возможность двух потоков, обновляющих разные грузовики одновременно, поскольку каждый поток, если он оставлен на своих собственных устройствах, создавал бы новый объект состояния флота, который включал бы новое состояние своего грузовика и старые состояния друг друга. Правильность может быть гарантирована, если каждый поток использует CompareExchange для обновления AllTrucks только в том случае, если он не изменился и отвечает на неудачный CompareExchange путем регенерации его объект состояния и повторная операция, но если более чем один поток пытается выполнить операцию одновременной записи, производительность, как правило, будет хуже, чем если бы все записи выполнялись в одном потоке; чем больше потоков предпринимает такие одновременные операции, тем хуже будет производительность.

Если отдельные объекты грузовика изменяются, но имеют неизменяемые идентификаторы , многопоточный сценарий становится более чистым. Одному потоку может быть разрешено работать одновременно на любом данном грузовике, но потоки, работающие на разных грузовиках, могут делать это без помех. Хотя есть способы эмулировать такое поведение даже при использовании неизменяемых объектов (например, можно было бы определить объекты «AllTrucks», так что установление состояния грузовика, принадлежащего XXX к SSS, просто потребовало бы создания объекта, в котором говорилось «С момента [Time]), состояние грузовика, принадлежащего [XXX], теперь [SSS], состояние всего остального - [Старое значение AllTrucks] ». Создание такого объекта было бы достаточно быстрым, чтобыдаже при наличии конкуренции, цикл CompareExchange не займет много времени. С другой стороны, использование такой структуры данных существенно увеличило бы время, необходимое для нахождения грузовика конкретного человека. Использование изменяемых объектов с неизменяемыми тождествами устраняет эту проблему.

ответил Marius Balčytis 12 +03002016-10-12T00:59:59+03:00312016bEurope/MoscowWed, 12 Oct 2016 00:59:59 +0300 2016, 00:59:59
8

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

Я думаю, что лучший и самый быстрый способ ответить на ваш вопрос - вы должны возглавить Плюсы и минусы неизменности против взаимности .

ответил Marius Balčytis 12 +03002016-10-12T00:59:59+03:00312016bEurope/MoscowWed, 12 Oct 2016 00:59:59 +0300 2016, 00:59:59
7

Отметьте это сообщение в блоге: http://www.yegor256.com/2014/06/09 /объекты-должны-быть-immutable.html . Он суммирует, почему неизменяемые объекты лучше, чем изменчивые. Ниже приведен короткий список аргументов:

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

Люди используют изменяемые объекты, насколько я понимаю, потому что они все еще смешивают ООП с императивным процедурным программированием.

ответил Marius Balčytis 12 +03002016-10-12T00:59:59+03:00312016bEurope/MoscowWed, 12 Oct 2016 00:59:59 +0300 2016, 00:59:59
5

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

Есть четыре проблемы с этим, главным образом, с привязкой данных :

  1. Метаданные отражения метаданных Java не сохраняют имена аргументов.
  2. Конструкторы (и методы) Java не имеют именованных параметров (также называемых ярлыками), поэтому он запутывает многие параметры.
  3. При наследовании другого неизменяемого объекта должен быть вызван правильный порядок конструкторов. Это может быть довольно сложно до такой степени, что нужно просто отказаться от одного из полей, которые не являются окончательными.
  4. Большинство технологий привязки (например, привязка данных Spring MVC, Hibernate и т. д.) будут работать только с конструкторами по умолчанию no-arg (это связано с тем, что аннотации не всегда существуют).

Вы можете уменьшить # 1 и # 2, используя аннотации, такие как @ ConstructorProperties и создание другого изменяемого объекта-строителя (обычно свободного) для создания неизменяемого объекта.

ответил Marius Balčytis 12 +03002016-10-12T00:59:59+03:00312016bEurope/MoscowWed, 12 Oct 2016 00:59:59 +0300 2016, 00:59:59
5

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

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

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

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

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

ответил Marius Balčytis 12 +03002016-10-12T00:59:59+03:00312016bEurope/MoscowWed, 12 Oct 2016 00:59:59 +0300 2016, 00:59:59
4

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

ответил Marius Balčytis 12 +03002016-10-12T00:59:59+03:00312016bEurope/MoscowWed, 12 Oct 2016 00:59:59 +0300 2016, 00:59:59
4

Языки программирования предназначены для выполнения компьютерами. Все важные строительные блоки компьютеров - ЦП, ОЗУ, кеш, диски - изменяемы. Когда они не являются (BIOS), они действительно неизменяемы, и вы также не можете создавать новые неизменные объекты.

Следовательно, любой язык программирования, построенный поверх неизменяемых объектов, страдает от недостатка представления в его реализации. И для ранних языков, таких как C, это был большой камень преткновения.

ответил Marius Balčytis 12 +03002016-10-12T00:59:59+03:00312016bEurope/MoscowWed, 12 Oct 2016 00:59:59 +0300 2016, 00:59:59
1

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

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

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

Таким образом, не-потокобезопасные, изменяемые объекты являются прекрасными, если вы не используете несколько потоков. (Но многопоточность надувается повсюду - будьте осторожны!) Их можно использовать безопасно иначе, если вы ограничите их локальными переменными или строго синхронизируйте их. Если вы можете избежать их, используя проверенный код других людей (БД, системные вызовы и т. Д.), Сделайте это. Если вы можете использовать неизменяемый класс, сделайте это. И я думаю , в general , люди либо не знают о проблемах многопоточности, либо , либо (ужасно) в ужасе от них и используют всевозможные трюки избегайте многопоточности (или, вернее, подталкивая ответственность за нее в другом месте).

Как P.S., я чувствую, что Java-получатели и сеттеры выходят из-под контроля. Отметьте .

ответил Marius Balčytis 12 +03002016-10-12T00:59:59+03:00312016bEurope/MoscowWed, 12 Oct 2016 00:59:59 +0300 2016, 00:59:59
1

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

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

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

Многие классы изменяются после создания, что хорошо, просто не нужно, чтобы его свойства напрямую обрабатывались - вместо этого его следует попросить манипулировать его свойствами посредством вызовов методов с фактической бизнес-логикой в ​​них (да, сеттер довольно почти то же самое, что непосредственно манипулировать свойством)

Теперь это не относится к коду /языку стиля «Скриптинг», но для кода, который вы создаете для кого-то другого, и ожидайте, что другие люди будут читать многократно на протяжении многих лет. Я должен был начать делать это различие в последнее время, потому что мне очень нравится возиться с Groovy, и есть огромная разница в цели.

ответил Marius Balčytis 12 +03002016-10-12T00:59:59+03:00312016bEurope/MoscowWed, 12 Oct 2016 00:59:59 +0300 2016, 00:59:59
1

Mutable objects используются, когда вам нужно установить значения мультипликатора после создания экземпляра объекта.

У вас не должно быть конструктора с, скажем, шестью параметрами. Вместо этого вы изменяете объект с помощью методов setter.

Примером этого является объект Report с настройками шрифта, ориентацией и т. д.

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

EDIT: шаблон Builder может использоваться для создания всего состояния объекта.

ответил Marius Balčytis 12 +03002016-10-12T00:59:59+03:00312016bEurope/MoscowWed, 12 Oct 2016 00:59:59 +0300 2016, 00:59:59
1

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

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

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

Компромисс, который я считаю наиболее разумным, - это: Начните с неизменяемых объектов, а затем переключитесь на изменчивые, если ваша реализация не будет достаточно быстрой. С этой точки зрения, использование изменчивых объектов систематически с самого начала можно рассматривать как своего рода <сильную> преждевременную оптимизацию : вы выбираете более эффективную (но еще более трудную для понимания и отладки) реализацию с самого начала.

Итак, почему многие программисты используют изменяемые объекты? ИМХО по двум причинам:

  1. Многие программисты научились программировать с использованием императивной (процедурной или объектно-ориентированной) парадигмы, поэтому изменчивость - это их основной подход к определению вычислений, т. е. они не знают, когда и как использовать неизменяемость, потому что они не знакомы с ним .
  2. Многие программисты беспокоятся о производительности слишком рано, тогда как часто более эффективно сначала сосредоточиться на написании функционально правильной программы, а затем попытаться найти узкие места и оптимизировать ее.
ответил Marius Balčytis 12 +03002016-10-12T00:59:59+03:00312016bEurope/MoscowWed, 12 Oct 2016 00:59:59 +0300 2016, 00:59:59
1

Я знаю, что вы спрашиваете о Java, но я использую mutable vs неизменяемый все время в объективе-c. Существует неизменный массив NSArray и изменяемый массив NSMutableArray. Это два разных класса, написанных специально оптимизированным способом для точного использования. Если мне нужно создать массив и никогда не изменять его содержимое, я бы использовал NSArray, который является меньшим объектом и намного быстрее в том, что он делает, по сравнению с изменяемым массивом.

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

Итак: в зависимости от того, что вы планируете делать с объектом, выбор mutable vs immutable может иметь огромное значение, когда дело доходит до производительности.

ответил Marius Balčytis 12 +03002016-10-12T00:59:59+03:00312016bEurope/MoscowWed, 12 Oct 2016 00:59:59 +0300 2016, 00:59:59
1

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

Это намного проще в языках, сделанных с неизменностью. Рассмотрим этот фрагмент Scala для определения класса Person, необязательно с именованными аргументами и создания копии с измененным атрибутом:

case class Person (id: String, firstName: String, lastName: String)

val joe = Person ("123", "Joe", "Doe")
val spouse = Person (id = "124", firstName = "Mary", lastName = "Moe")
val joeMarried = joe.copy (lastName = "Doe-Moe")

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

ответил Marius Balčytis 12 +03002016-10-12T00:59:59+03:00312016bEurope/MoscowWed, 12 Oct 2016 00:59:59 +0300 2016, 00:59:59
0

Неизменяемый означает, что вы не можете изменить значение, а mutable означает, что вы можете изменить значение, если вы думаете в терминах примитивов и объектов. Объекты отличаются от примитивов в Java тем, что они построены в типах. Примитивы строятся в таких типах, как int, boolean и void.

Многие люди думают, что переменные примитивов и объектов, которые имеют окончательный модификатор infront из них, неизменяемы, однако это не совсем так. Таким образом, окончательный почти не означает неизменяемость переменных. Проверьте эту ссылку для образца кода:

публичный абстрактный класс FinalBase {

    частная конечная переменная int; //Отменить

    /* если final действительно означает неизменяемость,
     * Я не могу установить переменную
     * Но я могу.
     * /
    public FinalBase (int variable) {
        this.variable = variable;
    }

    public int getVariable () {
        возвращаемая переменная;
    }

    открытый абстрактный метод void ();
}

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

    public FinalSubclass (int variable) {
        супер (переменная);
    }

    @Override
    public void method () {
        System.out.println (getVariable ());
    }

    @Override
    public int getVariable () {

        return super.getVariable ();
    }

    public static void main (String [] args) {
        FinalSubclass subclass = новый FinalSubclass (10);
        subclass.method ();
    }
}
ответил Marius Balčytis 12 +03002016-10-12T00:59:59+03:00312016bEurope/MoscowWed, 12 Oct 2016 00:59:59 +0300 2016, 00:59:59
-1

Я думаю, что хорошей причиной было бы моделирование «реальных» изменяемых «объектов», таких как окна интерфейса. Я смутно помню, что прочитал, что ООП был изобретен, когда кто-то пытался написать программное обеспечение для управления работой какого-либо грузового порта.

ответил Marius Balčytis 12 +03002016-10-12T00:59:59+03:00312016bEurope/MoscowWed, 12 Oct 2016 00:59:59 +0300 2016, 00:59:59
-2

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

ответил Marius Balčytis 12 +03002016-10-12T00:59:59+03:00312016bEurope/MoscowWed, 12 Oct 2016 00:59:59 +0300 2016, 00:59:59

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132