Почему фабричные методы для коллекций производят неизменные экземпляры? [Дубликат]

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

Допустим, у вас есть список ссылок и при падении на одном, все предыдущие должны стать красными. Вы можете сделать это следующим образом:

/* default link color is blue */
.parent a {
  color: blue;
}

/* prev siblings should be red */
.parent:hover a {
  color: red;
}
.parent a:hover,
.parent a:hover ~ a {
  color: blue;
}
<div class="parent">
  <a href="#">link</a>
  <a href="#">link</a>
  <a href="#">link</a>
  <a href="#">link</a>
  <a href="#">link</a>
</div>

63
задан Rakesh Juyal 22 September 2010 в 14:17
поделиться

20 ответов

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

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

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

Вы когда-нибудь сталкивались с таким требованием?

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

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

Вы можете лучше понять, где неизменяемые объекты действительно полезно (если не строго необходимо), убедившись, что вы читаете в разных книгах / онлайн-статьях, чтобы развить здравый смысл о том, как думать о непреложных занятиях. Одна хорошая статья, чтобы вы начали: Теория и практика Java: мутировать или не мутировать?

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

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

< / blockquote>

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

Классические объекты значения

Строки и целые числа и часто считаются ценностями. Поэтому неудивительно, что класс String и класс оболочки Integer (а также другие классы-оболочки) неизменяемы в Java. Цвет обычно считается значением, поэтому неизменный класс Color.

Counterexample

Напротив, автомобиль обычно не рассматривается как объект ценности. Моделирование автомобиля обычно означает создание класса, который меняет состояние (одометр, скорость, уровень топлива и т. Д.). Тем не менее, есть некоторые домены, где автомобиль может быть объектом ценности. Например, автомобиль (или, в частности, модель автомобиля) можно рассматривать как объект ценности в приложении для поиска правильного моторного масла для данного автомобиля.

Игральные карты

Когда-нибудь пишете программу игровых карт? Я сделал. Я мог бы представить игральную карту как изменчивый объект с изменчивым костюмом и рангом. Рука ничьей-покера может быть 5 фиксированных экземпляров, где замена пятой карты в моей руке означала бы переустановку экземпляра пятой игровой карты на новую карту, изменив ее костюм и ранги ivars.

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

Map Projection

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

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

65
ответ дан Bert F 18 August 2018 в 02:22
поделиться

Эффективная Java от Джошуа Блоха описывает несколько причин для написания неизменяемых классов:

  • Простота - каждый класс находится только в одном состоянии
  • Thread Safe - потому что состояние не может быть изменено, синхронизация не требуется
  • Запись в неизменяемом стиле может привести к созданию более надежного кода. Представьте, что Струны не были неизменными; Любые методы getter, которые возвращали String, потребовали бы, чтобы реализация создала защитную копию до того, как была возвращена String, иначе клиент может случайно или злонамеренно нарушить это состояние объекта.

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

36
ответ дан Amir Raminfar 18 August 2018 в 02:22
поделиться

Одной из причин «необходимости» для неизменяемых классов является комбинация передачи всего по ссылке и отсутствие поддержки просмотра только для чтения объекта (например, C ++ const).

Рассмотрим простой случай класса, имеющего поддержку шаблона наблюдателя:

class Person {
    public string getName() { ... }
    public void registerForNameChange(NameChangedObserver o) { ... }
}

Если string не были неизменяемы, для класса Person было бы невозможно реализовать registerForNameChange() правильно, потому что

void foo(Person p) {
    p.getName().prepend("Mr. ");
}

В C ++ getName(), возвращающий const std::string&, имеет эффект возврата по ссылке и предотвращения доступа к мутаторам, подразумевая, что в этом контексте необязательные классы не нужны.

1
ответ дан André Caron 18 August 2018 в 02:22
поделиться

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

  • Вы можете использовать несколько ядро ​​/ обработку (параллельную обработку) с помощью
  • Может выполнять кэширование для дорогостоящей работы (поскольку вы уверены в том же самом результате).
  • Можно легко отлаживать (поскольку история запуска больше не будет заботой)
0
ответ дан Arun Pratap Singh 18 August 2018 в 02:22
поделиться

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

Представьте, как выглядит Java, если String изменен.

7
ответ дан BalusC 18 August 2018 в 02:22
поделиться
  • 1
    Или, если Date и Calendar являются изменяемыми. О, подождите, они, OH SH – gustafc 22 September 2010 в 14:23
  • 2
    @gustafc: Календар, изменяющийся, в порядке, учитывая его работу по вычислению даты (может быть сделано путем возврата копий, но с учетом того, насколько тяжелым является календарь в календаре, тем лучше). но дата - да, это противно. – Michael Borgwardt 22 September 2010 в 14:50
  • 3
    На некоторых реализациях JRE String изменчиво! (подсказка: некоторые старые версии JRockit). Вызов string.trim () привел к тому, что исходная строка была обрезана – Salandur 22 September 2010 в 15:10
  • 4
    @Salandur: Тогда это не «реализация JRE». Это реализация чего-то, что напоминает JRE, но это не так. – Mark Peters 22 September 2010 в 15:57
  • 5
    @Mark Peters: очень истинное утверждение – Salandur 23 September 2010 в 11:28

Почему нерушимый класс?

После создания экземпляра объекта состояние не может быть изменено в течение жизни. Это также делает его потокобезопасным.

Примеры:

Очевидно, что String, Integer и BigDecimal и т. Д. После того, как эти значения будут созданы, нельзя изменить в течение жизни.

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

0
ответ дан bharanitharan 18 August 2018 в 02:22
поделиться

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

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

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

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

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

1
ответ дан Claudiu 18 August 2018 в 02:22
поделиться

Неизменяемые классы в целом гораздо проще в проектировании, внедрении и правильном использовании. Примером является String: реализация java.lang.String значительно проще, чем реализация std::string в C ++, в основном из-за ее неизменности.

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

Обновление: Эффективное Java 2nd Edition решает эту проблему подробнее - см. Пункт 15: Минимизировать изменчивость .

См. также эти связанные сообщения:

39
ответ дан Community 18 August 2018 в 02:22
поделиться

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

6
ответ дан Damien_The_Unbeliever 18 August 2018 в 02:22
поделиться

Возьмем крайний случай: целочисленные константы. Если я напишу заявление типа «x = x + 1», я хочу быть на 100% доверенным, что число «1» не будет каким-то образом становиться 2, независимо от того, что происходит в другом месте программы.

Теперь okay, целочисленные константы не являются классом, но концепция одинаков. Предположим, что я пишу:

String customerId=getCustomerId();
String customerName=getCustomerName(customerId);
String customerBalance=getCustomerBalance(customerid);

Выглядит достаточно просто. Но если строки не были неизменными, тогда мне пришлось бы учитывать возможность того, что getCustomerName может изменить customerId, так что, когда я вызываю getCustomerBalance, я получаю баланс для другого клиента. Теперь вы можете сказать: «Почему в мире кто-то, пишущий функцию getCustomerName, изменит идентификатор? Это не имеет смысла». Но это именно то, где вы можете попасть в беду. Человек, написавший вышеуказанный код, может считать это просто очевидным, что функции не изменят параметр. Затем приходит кто-то, кто должен изменить другое использование этой функции, чтобы обрабатывать случай, когда клиент имеет несколько учетных записей под тем же именем. И он говорит: «О, вот эта удобная функция имени getCustomer, которая уже ищет имя. Я просто сделаю это, чтобы автоматически изменить идентификатор на следующую учетную запись с тем же именем и поставить ее в цикл ...» И то ваша программа начинает таинственно не работать. Будет ли это плохой стиль кодирования? Вероятно. Но это точно проблема в тех случаях, когда побочный эффект НЕ очевиден.

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

( Конечно, пользователь может назначить переменную «постоянный объект» для переменной. Кто-то может написать String s = «hello», а затем написать s = «goodbye»; если я не сделаю переменную final, я не могу быть уверен, что он не изменяется в моем собственном блоке кода. Как и целочисленные константы, заверить меня, что «1» всегда совпадает с номером, но не «x = 1» никогда не будет изменен, написав «x = 2». быть уверенным в том, что если у меня есть дескриптор неизменяемого объекта, то никакая функция, которую я передаю, не может изменить его на мне, или что если я сделаю две копии этого, то изменение переменной, содержащей одну копию, не изменит другую И др.

5
ответ дан Jay 18 August 2018 в 02:22
поделиться

Существуют разные причины неизменности:

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

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

5
ответ дан John Topley 18 August 2018 в 02:22
поделиться
  • 1
    Я не участвую в отправке по сети, а также в той части, в которой вы говорите, что неизменяемый класс не может быть расширен. – aioobe 22 September 2010 в 14:45
  • 2
    @aioobe, посылая данные по сети, например. Веб-сервисы, RMI и т. Д. И для расширенных вы не можете расширить неизменяемый класс String или ImmutableSet, ImmutableList и т. Д. – Buhake Sindi 22 September 2010 в 14:58
  • 3
    Невозможность расширения String, ImmutableSet, ImmutableList и т. Д. Является полностью ортогональной проблемой неизменности. Не все классы, помеченные final в Java, неизменяемы, и не все неизменяемые классы помечены final. – JUST MY correct OPINION 22 September 2010 в 16:09

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

Если у меня есть изменяемый объект, я не уверен, какова его ценность, если он когда-либо используется за пределами моей непосредственной области. Предположим, я создаю MyMutableObject в локальных переменных метода, заполняю их значениями, а затем передаю их еще пяти методам. Любой из этих методов может изменить состояние моего объекта, поэтому должно произойти одно из двух:

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

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

Если я вместо этого использую MyImmutableObject, я могу быть уверен, что то, что я установил, - это то, что значения будут для жизни моего метода. На расстоянии нет «жутких действий», которые меняют его из-под меня, и мне не нужно делать защитные копии моего объекта, прежде чем ссылаться на пять других методов. Если другие методы хотят изменить вещи для своих целей, они должны сделать копию & ndash; но они делают это только в том случае, если им действительно нужно сделать копию (в отличие от моего выполнения перед каждым вызовом внешних методов). Я избавляю себя от умственных способностей отслеживать методы, которые могут быть даже не в моем текущем исходном файле, и я избавляю систему от накладных расходов, бесконечно делая ненужные защитные копии на всякий случай.

(Если я пойду вне мира Java и, скажем, в мире C ++, я могу стать еще более сложным. Я могу сделать объекты похожими, как будто они изменяемы, но за кулисами делают их прозрачно клонированными при любом изменении состояния & mdash , это copy-on-write & mdash, причем никто не мудрее.)

5
ответ дан JUST MY correct OPINION 18 August 2018 в 02:22
поделиться
  • 1
    Одно из преимуществ изменяемых типов значений в языках, которые их поддерживают, и не позволяют постоянно ссылаться на них, заключается в том, что вы получаете преимущества, которые вы описываете (если изменяемый ссылочный тип передается ссылкой на подпрограмму, эта подпрограмма может изменять ее во время ее запуска , но не может вызывать изменений после его возврата) без некоторой неловкости, которую часто имеют неизменяемые объекты. – supercat 10 February 2012 в 03:33

Хашмапы - классический пример. Крайне важно, чтобы ключ к карте был неизменным. Если ключ не является неизменным, и вы изменяете значение на ключе, так что hashCode () приведет к новому значению, карта теперь сломана (теперь ключ находится в неправильном месте в хэш-таблице).

11
ответ дан Kirk Woll 18 August 2018 в 02:22
поделиться
  • 1
    Я бы скорее сказал, что важно, чтобы ключ не был изменен; нет никаких официальных требований, чтобы он был неизменным. – Péter Török 22 September 2010 в 14:29
  • 2
    Не знаете, что вы подразумеваете под «официальным». – Kirk Woll 22 September 2010 в 14:38
  • 3
    Я думаю, что он имеет в виду, единственное реальное требование состоит в том, что ключи НЕ ДОЛЖНЫ быть изменены ... не то, что они НЕ МОГУТ быть изменены вообще (через неизменность). Конечно, простой способ предотвратить изменение ключей - это просто сделать их неизменными в первую очередь! :-) – Platinum Azure 22 September 2010 в 14:51
  • 4
    Например. download.oracle.com/javase/6/docs/api/java/util/Map.html : & quot; Примечание. Следует проявлять большую осторожность, если изменяемые объекты используются в качестве клавиш карты ". То есть в качестве ключей можно использовать изменяемые объекты can . – Péter Török 22 September 2010 в 14:54

Использование ключевого слова final не обязательно делает что-то неизменяемым:

public class Scratchpad {
    public static void main(String[] args) throws Exception {
        SomeData sd = new SomeData("foo");
        System.out.println(sd.data); //prints "foo"
        voodoo(sd, "data", "bar");
        System.out.println(sd.data); //prints "bar"
    }

    private static void voodoo(Object obj, String fieldName, Object value) throws Exception {
        Field f = SomeData.class.getDeclaredField("data");
        f.setAccessible(true);
        Field modifiers = Field.class.getDeclaredField("modifiers");
        modifiers.setAccessible(true);
        modifiers.setInt(f, f.getModifiers() & ~Modifier.FINAL);
        f.set(obj, "bar");
    }
}

class SomeData {
    final String data;
    SomeData(String data) {
        this.data = data;
    }
}

Просто пример, демонстрирующий, что ключевое слово «final» существует, чтобы предотвратить ошибку программиста и не намного больше. В то время как переназначение значения, не имеющего ключевого слова final, может легко произойти случайно, идя на эту длину, чтобы изменить значение, нужно было бы сделать намеренно. Это там для документации и для предотвращения ошибки программиста.

1
ответ дан Mark Peters 18 August 2018 в 02:22
поделиться
  • 1
    Обратите внимание, что это прямо не отвечает на вопрос, поскольку речь идет не о финале; речь шла о непреложных занятиях. У вас может быть неизменяемый класс без ключевого слова final, используя правильные ограничения доступа. – Mark Peters 22 September 2010 в 16:18

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

http://en.wikipedia.org/wiki/Singleton_pattern

1
ответ дан Ryan Hayes 18 August 2018 в 02:22
поделиться

Неизменяемыми объектами являются экземпляры, чьи состояния не меняются после инициализации. Использование таких объектов является специфическим требованием.

Непрерывный класс хорош для цели кеширования и является потокобезопасным.

1
ответ дан Shreya Agarwal 18 August 2018 в 02:22
поделиться

Одна особенность неизменяемых классов, которые еще не были вызваны: сохранение ссылки на объект с неизменяемым классом является эффективным средством хранения всего содержащегося в нем состояния. Предположим, что у меня есть изменяемый объект, который использует глубоко неизменный объект для хранения информации о состоянии на 50 КБ. Предположим, далее, что я хочу в 25 случаях сделать «копию» моего исходного (изменяемого) объекта (например, для «отменить»); состояние может меняться между операциями копирования, но обычно этого не происходит. Создание «копии» изменяемого объекта просто потребовало бы копирования ссылки на его неизменяемое состояние, поэтому 20 экземпляров просто составят 20 ссылок. В отличие от этого, если бы государство было размещено на 50 тыс. Измененных объектов, каждая из 25 операций копирования должна была создать свою собственную копию данных на 50 тыс. для хранения всех 25 экземпляров потребовалось бы хранить большую сумму дублированных данных. Несмотря на то, что первая операция копирования создаст копию данных, которые никогда не будут меняться, а остальные 24 операции могут теоретически просто ссылаться на это, в большинстве реализаций не было бы способа, чтобы второй объект запрашивал копию чтобы узнать, что неизменяемая копия уже существует (*).

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

1
ответ дан supercat 18 August 2018 в 02:22
поделиться

из Effective Java; Неизменяемый класс - это просто класс, экземпляры которого не могут быть изменены. Вся информация, содержащаяся в каждом экземпляре, предоставляется при ее создании и фиксируется для времени жизни объекта. Библиотеки платформы Java содержат множество неизменяемых классов, включая String, классы в штучной упаковке и BigInteger и BigDecimal. Для этого есть много веских причин: неизменяемые классы легче разрабатывать, реализовывать и использовать, чем изменяемые классы. Они менее подвержены ошибкам и более безопасны.

0
ответ дан tarikfasun 18 August 2018 в 02:22
поделиться
0
ответ дан hagrawal 6 September 2018 в 17:30
поделиться
0
ответ дан hagrawal 29 October 2018 в 23:24
поделиться
Другие вопросы по тегам:

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