Я нашел способ сфокусировать всех предыдущих братьев и сестер (напротив ~
), которые могут работать в зависимости от того, что вам нужно.
Допустим, у вас есть список ссылок и при падении на одном, все предыдущие должны стать красными. Вы можете сделать это следующим образом:
/* 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>
Другие ответы, похоже, были сфокусированы на объяснении того, почему неизменяемость хороша. Это очень хорошо, и я использую его, когда это возможно. Однако это не ваш вопрос . Я возьму ваш вопрос по пунктам, чтобы убедиться, что вы получаете ответы и примеры, которые вам нужны.
Я не могу получить то, что является сценариями, в которых нам нужен неизменный класс .
«Need» - относительный термин здесь. Неизменяемые классы - это шаблон дизайна, который, как и любая парадигма / шаблон / инструмент, позволяет упростить создание программного обеспечения. Точно так же много кода было написано до того, как появилась парадигма OO, но посчитайте меня среди программистов, которые «нуждаются» OO. Неизменяемые классы, такие как OO, не являются строго необходимыми , но я буду действовать так, как они мне нужны.
Вы когда-нибудь сталкивались с таким требованием?
Если вы не смотрите объекты в проблемной области с правильной перспективой, вы можете не увидеть требование для неизменяемого объекта. Было бы легко подумать, что проблемная область не требует любых любых неизменяемых классов, если вы не знакомы, когда их использовать выгодно.
Я часто использую неизменяемые классы, где я подумайте об определенном объекте в моей проблемной области как значение или фиксированный экземпляр. Это понятие иногда зависит от перспективы или точки зрения, но в идеале легко будет переключиться в правильную перспективу для идентификации хороших объектов-кандидатов.
Вы можете лучше понять, где неизменяемые объекты действительно полезно (если не строго необходимо), убедившись, что вы читаете в разных книгах / онлайн-статьях, чтобы развить здравый смысл о том, как думать о непреложных занятиях. Одна хорошая статья, чтобы вы начали: Теория и практика Java: мутировать или не мутировать?
Я попытаюсь привести несколько примеров ниже того, как можно видеть объекты в разных перспективах (изменяемые и неизменные), чтобы уточнить, что я имею в виду под перспективой.
... можете ли вы привести нам какой-либо реальный пример, где мы должны использовать этот шаблон.
< / blockquote>Поскольку вы попросили дать реальные примеры, я дам вам некоторые, но сначала начнем с некоторых классических примеров.
Классические объекты значения
Строки и целые числа и часто считаются ценностями. Поэтому неудивительно, что класс String и класс оболочки Integer (а также другие классы-оболочки) неизменяемы в Java. Цвет обычно считается значением, поэтому неизменный класс Color.
Counterexample
Напротив, автомобиль обычно не рассматривается как объект ценности. Моделирование автомобиля обычно означает создание класса, который меняет состояние (одометр, скорость, уровень топлива и т. Д.). Тем не менее, есть некоторые домены, где автомобиль может быть объектом ценности. Например, автомобиль (или, в частности, модель автомобиля) можно рассматривать как объект ценности в приложении для поиска правильного моторного масла для данного автомобиля.
Игральные карты
Когда-нибудь пишете программу игровых карт? Я сделал. Я мог бы представить игральную карту как изменчивый объект с изменчивым костюмом и рангом. Рука ничьей-покера может быть 5 фиксированных экземпляров, где замена пятой карты в моей руке означала бы переустановку экземпляра пятой игровой карты на новую карту, изменив ее костюм и ранги ivars.
Однако я склонен подумайте о игральной карте как непреложном объекте, у которого есть фиксированный неизменный костюм и ранг после создания. Моя рука в покере будет 5 экземпляров, и замена карты в моей руке приведет к отбрасыванию одного из этих экземпляров и добавлению нового случайного экземпляра в мою руку.
Map Projection
Последний Например, когда я работал над некоторым кодом карты, где карта могла отображаться в различных проекциях . В исходном коде карта использовала фиксированный, но мутируемый экземпляр проекции (например, измененную игровую карту выше). Изменение проекции карты означало мутирование иваров экземпляра проекции карты (тип проекции, центральная точка, масштабирование и т. Д.).
Однако я чувствовал, что дизайн был проще, если я думал о проекции как неизменяемом значении или фиксированном пример. Изменение проекции карты означало наличие ссылки на карту другого экземпляра проекции, а не мутирования фиксированного проекционного экземпляра карты. Это также упростило захват названных проекций, таких как
MERCATOR_WORLD_VIEW
.
Эффективная Java от Джошуа Блоха описывает несколько причин для написания неизменяемых классов:
В целом это является хорошей практикой сделать объект неизменным, если в результате не возникнут серьезные проблемы с производительностью. В таких обстоятельствах изменяемые объекты-строители могут использоваться для создания неизменяемых объектов, например. StringBuilder
Одной из причин «необходимости» для неизменяемых классов является комбинация передачи всего по ссылке и отсутствие поддержки просмотра только для чтения объекта (например, 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&
, имеет эффект возврата по ссылке и предотвращения доступа к мутаторам, подразумевая, что в этом контексте необязательные классы не нужны.
По неизменности вы можете быть уверены, что поведение не изменится, и вы получите дополнительное преимущество от выполнения дополнительных операций:
Java - это практически одна и все ссылки. Иногда экземпляр ссылается несколько раз. Если вы измените такой экземпляр, он будет отражен во всех его ссылках. Иногда вы просто не хотите, чтобы это улучшало надежность и безопасность потоков. Тогда неизменяемый класс полезен, поэтому необходимо создать новый экземпляр и переназначить его в текущую ссылку. Таким образом, исходный экземпляр других ссылок остается нетронутым.
Представьте, как выглядит Java, если String
изменен.
Date
и Calendar
являются изменяемыми. О, подождите, они, OH SH
– gustafc
22 September 2010 в 14:23
String
изменчиво! (подсказка: некоторые старые версии JRockit). Вызов string.trim () привел к тому, что исходная строка была обрезана
– Salandur
22 September 2010 в 15:10
Почему нерушимый класс?
blockquote>После создания экземпляра объекта состояние не может быть изменено в течение жизни. Это также делает его потокобезопасным.
Примеры:
blockquote>Очевидно, что String, Integer и BigDecimal и т. Д. После того, как эти значения будут созданы, нельзя изменить в течение жизни.
Пример использования: после создания объекта подключения к базе данных с его значениями конфигурации вам может не потребоваться изменить его состояние, где вы можете использовать неизменяемый класс
blockquote>
Непрерывные структуры данных также могут помочь при кодировании рекурсивных алгоритмов. Например, скажите, что вы пытаетесь решить проблему 3SAT . Один из способов состоит в следующем:
Если у вас есть изменяемая структура для представления проблемы, тогда, когда вы упрощаете экземпляр в ветке TRUE, вам либо придется :
Однако, если вы закодируете это умным способом, вы может иметь неизменяемую структуру, где любая операция возвращает обновленную (но все еще неизменяемую) версию проблемы (аналогично String.replace
- она не заменяет строку, просто дает вам новую). Наивный способ реализовать это состоит в том, чтобы «неизменяемая» структура просто копировала и создавала новую для любых модификаций, сводя ее ко второму решению, имея сменную, со всеми этими накладными расходами, но вы можете сделать это в более эффективным способом.
Неизменяемые классы в целом гораздо проще в проектировании, внедрении и правильном использовании. Примером является String: реализация java.lang.String
значительно проще, чем реализация std::string
в C ++, в основном из-за ее неизменности.
Одна особая область, где неизменность делает особенно большую разницу, - это параллелизм: неизменный объекты могут безопасно распределяться между несколькими потоками, тогда как изменяемые объекты должны быть потокобезопасными посредством тщательного проектирования и реализации - обычно это далеко не тривиальная задача.
Обновление: Эффективное Java 2nd Edition решает эту проблему подробнее - см. Пункт 15: Минимизировать изменчивость .
См. также эти связанные сообщения:
Мы не нуждаемся в неизменяемых классах, как таковые, но они, безусловно, могут облегчить некоторые задачи программирования, особенно если задействованы несколько потоков. Вам не нужно выполнять блокировку для доступа к неизменяемому объекту, и любые факты, которые вы уже установили в отношении такого объекта, по-прежнему будут иметь значение в будущем.
Возьмем крайний случай: целочисленные константы. Если я напишу заявление типа «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». быть уверенным в том, что если у меня есть дескриптор неизменяемого объекта, то никакая функция, которую я передаю, не может изменить его на мне, или что если я сделаю две копии этого, то изменение переменной, содержащей одну копию, не изменит другую И др.
Существуют разные причины неизменности:
String
. Итак, если вы хотите отправлять данные через сетевую услугу, и вы хотите получить гарантию того, что у вас будет ваш результат точно так же, как и то, что вы отправили , установите его как неизменяемый.
final
в Java, неизменяемы, и не все неизменяемые классы помечены final
.
– JUST MY correct OPINION
22 September 2010 в 16:09
Я собираюсь атаковать это с другой точки зрения. Я нахожу неизменные объекты, которые облегчают мне жизнь при чтении кода.
Если у меня есть изменяемый объект, я не уверен, какова его ценность, если он когда-либо используется за пределами моей непосредственной области. Предположим, я создаю MyMutableObject
в локальных переменных метода, заполняю их значениями, а затем передаю их еще пяти методам. Любой из этих методов может изменить состояние моего объекта, поэтому должно произойти одно из двух:
Первое затрудняет рассуждение о моем коде. Во-вторых, мой код вписывается в производительность - я в любом случае имитирую неизменяемый объект с семантикой copy-on-write, но все время делаю это независимо от того, действительно ли вызываемые методы изменяют состояние моего объекта.
Если я вместо этого использую MyImmutableObject
, я могу быть уверен, что то, что я установил, - это то, что значения будут для жизни моего метода. На расстоянии нет «жутких действий», которые меняют его из-под меня, и мне не нужно делать защитные копии моего объекта, прежде чем ссылаться на пять других методов. Если другие методы хотят изменить вещи для своих целей, они должны сделать копию & ndash; но они делают это только в том случае, если им действительно нужно сделать копию (в отличие от моего выполнения перед каждым вызовом внешних методов). Я избавляю себя от умственных способностей отслеживать методы, которые могут быть даже не в моем текущем исходном файле, и я избавляю систему от накладных расходов, бесконечно делая ненужные защитные копии на всякий случай.
(Если я пойду вне мира Java и, скажем, в мире C ++, я могу стать еще более сложным. Я могу сделать объекты похожими, как будто они изменяемы, но за кулисами делают их прозрачно клонированными при любом изменении состояния & mdash , это copy-on-write & mdash, причем никто не мудрее.)
Хашмапы - классический пример. Крайне важно, чтобы ключ к карте был неизменным. Если ключ не является неизменным, и вы изменяете значение на ключе, так что hashCode () приведет к новому значению, карта теперь сломана (теперь ключ находится в неправильном месте в хэш-таблице).
Использование ключевого слова 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, может легко произойти случайно, идя на эту длину, чтобы изменить значение, нужно было бы сделать намеренно. Это там для документации и для предотвращения ошибки программиста.
Они также дают нам гарантию. Гарантия неизменности означает, что мы можем расширять их и создавать новые паттеры для эффективности, которые в противном случае невозможны.
Неизменяемыми объектами являются экземпляры, чьи состояния не меняются после инициализации. Использование таких объектов является специфическим требованием.
Непрерывный класс хорош для цели кеширования и является потокобезопасным.
Одна особенность неизменяемых классов, которые еще не были вызваны: сохранение ссылки на объект с неизменяемым классом является эффективным средством хранения всего содержащегося в нем состояния. Предположим, что у меня есть изменяемый объект, который использует глубоко неизменный объект для хранения информации о состоянии на 50 КБ. Предположим, далее, что я хочу в 25 случаях сделать «копию» моего исходного (изменяемого) объекта (например, для «отменить»); состояние может меняться между операциями копирования, но обычно этого не происходит. Создание «копии» изменяемого объекта просто потребовало бы копирования ссылки на его неизменяемое состояние, поэтому 20 экземпляров просто составят 20 ссылок. В отличие от этого, если бы государство было размещено на 50 тыс. Измененных объектов, каждая из 25 операций копирования должна была создать свою собственную копию данных на 50 тыс. для хранения всех 25 экземпляров потребовалось бы хранить большую сумму дублированных данных. Несмотря на то, что первая операция копирования создаст копию данных, которые никогда не будут меняться, а остальные 24 операции могут теоретически просто ссылаться на это, в большинстве реализаций не было бы способа, чтобы второй объект запрашивал копию чтобы узнать, что неизменяемая копия уже существует (*).
(*) Один шаблон, который иногда может быть полезен, заключается в том, что изменяемые объекты имеют два поля для хранения своего состояния: одно в изменяемой форме и одно в неизменной форме. Объекты могут быть скопированы как изменчивые или неизменные и начнут жизнь с одного или другого набора ссылок. Как только объект хочет изменить свое состояние, он копирует неизменяемую ссылку на изменяемую (если она еще не была сделана) и делает недействительной неизменяемую. Когда объект копируется как неизменяемый, если его неизменяемая ссылка не задана, будет создана неизменяемая копия, и на нее будет ссылаться неизменная ссылка. Этот подход потребует еще нескольких операций копирования, чем «полноценная копия при записи» (например, запрос на копирование объекта, который был мутирован, поскольку для последней копии требуется операция копирования, даже если исходный объект больше не мутируется ), но это позволяет избежать сложностей потоков, которые повлечет FFCOW.
из Effective Java; Неизменяемый класс - это просто класс, экземпляры которого не могут быть изменены. Вся информация, содержащаяся в каждом экземпляре, предоставляется при ее создании и фиксируется для времени жизни объекта. Библиотеки платформы Java содержат множество неизменяемых классов, включая String, классы в штучной упаковке и BigInteger и BigDecimal. Для этого есть много веских причин: неизменяемые классы легче разрабатывать, реализовывать и использовать, чем изменяемые классы. Они менее подвержены ошибкам и более безопасны.