Важным моментом, который еще не упоминался, является то, что наличие изменяемого состояния объекта позволяет иметь неизменяемость идентификатора объекта, который инкапсулирует это состояние.
Многие программы предназначены для моделирования реальных вещей, которые по своей природе изменчивы. Предположим, что в 12:51 некоторая переменная AllTrucks
содержит ссылку на объект # 451, который является корнем структуры данных, которая указывает, какой груз содержится на всех грузовиках парка в этот момент (12:51 утра), и некоторую переменную BobsTruck
может использоваться для получения ссылки на объект № 24601, указывающей на объект, который указывает, какой груз содержится в грузовике Боба в тот момент (0:51 утра). В 12:52 некоторые грузовики (включая Боба) загружаются и выгружаются, и структуры данных обновляются, так что AllTrucks
теперь будет храниться ссылка на структуру данных, которая указывает, что груз находится во всех грузовиках по состоянию на 0:52.
Что должно случиться BobsTruck
?
Если свойство «Cargo» каждого объекта грузовика является неизменным, тогда объект № 24601 будет всегда представлять состояние, в котором находился грузовик Боба в 0:51. Если BobsTruck
содержит прямую ссылку на объект # 24601, то, если обновленный код AllTrucks
также не обновляется BobsTruck
, он перестанет представлять текущее состояние грузовика Боба. Отметим далее, что, если только он BobsTruck
не хранится в какой-либо форме изменяемого объекта, единственный код, который AllTrucks
может обновлять обновленный код, - это если бы код был явно запрограммирован для этого.
Если кто-то хочет иметь возможность BobsTruck
наблюдать состояние грузовика Боба, сохраняя при этом все объекты неизменяемыми, он может BobsTruck
быть неизменной функцией, которая, учитывая значение, которое AllTrucks
имеет или имела в любой конкретный момент времени, даст состояние грузовика Боба в то время. Можно даже иметь в нем пару неизменяемых функций, одна из которых будет такой же, как и выше, а другая будет принимать ссылку на состояние флота и новое состояние грузовика и возвращать ссылку на новое состояние флота, которое соответствует старому, за исключением того, что у грузовика Боба будет новое состояние.
К сожалению, необходимость использовать такую функцию каждый раз, когда кто-то хочет получить доступ к состоянию грузовика Боба, может стать довольно раздражающей и громоздкой. Альтернативный подход заключается в том, чтобы сказать, что объект № 24601 всегда и всегда (до тех пор, пока кто-либо имеет ссылку на него) представляет текущее состояние грузовика Боба. Код, который будет хотеть многократно обращаться к текущему состоянию грузовика Боба, не должен будет каждый раз запускать некоторую трудоемкую функцию - он может просто один раз выполнить функцию поиска, чтобы выяснить, что объект # 24601 является грузовиком Боба, а затем просто доступ к этому объекту в любое время, когда он хочет увидеть текущее состояние грузовика Боба.
Обратите внимание, что функциональный подход не лишен преимуществ в однопоточной среде или в многопоточной среде, где потоки будут в основном просто наблюдать за данными, а не изменять их. Любой поток наблюдателя, который копирует ссылку на объект, содержащийся вAllTrucks
а затем исследует состояния грузовиков, представленных таким образом, увидит состояние всех грузовиков на момент, когда он захватил ссылку. Каждый раз, когда поток наблюдателя хочет увидеть новые данные, он может просто повторно захватить ссылку. С другой стороны, наличие всего состояния парка, представленного одним неизменным объектом, исключает возможность одновременного обновления двумя грузовиками двух потоков, поскольку каждый поток, если оставить его для своих собственных устройств, будет создавать новый объект «состояние парка», который включает новое состояние его грузовика и старые состояния друг друга. Корректность может быть гарантирована, если каждый поток использует CompareExchange
для обновления, AllTrucks
только если он не изменился, и отвечает на сбойCompareExchange
путем регенерации объекта состояния и повторения операции, но если более чем один поток пытается выполнить операцию одновременной записи, производительность, как правило, будет хуже, чем если бы все записи выполнялись в одном потоке; чем больше потоков пытается выполнить такие одновременные операции, тем хуже будет производительность.
Если отдельные объекты грузовика изменчивы, но имеют неизменные идентификаторы , многопоточный сценарий становится чище. Только одна резьба может работать одновременно на любом грузовике, но нити, работающие на разных грузовиках, могут работать без помех. Хотя есть способы, которыми можно эмулировать такое поведение даже при использовании неизменяемых объектов (например, можно определить объекты «AllTrucks» так, чтобы для установки состояния грузовика, принадлежащего XXX, к SSS просто потребовалось бы создать объект, который говорит «По состоянию на [Время], состояние грузовика, принадлежащего [XXX], теперь равно [SSS]; состояние всего остального - [Старое значение AllTrucks] ". Создание такого объекта было бы достаточно быстрым, чтобы даже при наличии раздораCompareExchange
цикл не займет много времени. С другой стороны, использование такой структуры данных значительно увеличит время, необходимое для поиска грузовика конкретного человека. Использование изменяемых объектов с неизменяемыми идентификаторами позволяет избежать этой проблемы.