Неизменяемость и общие ссылки - как смириться?

Рассмотрим эту упрощенную область приложения:

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

Домен http://yuml.me/13fc6da0

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

Предположим, что я изменяю некоторые метаданные о Person. Поскольку мои объекты Person неизменяемы, у меня может быть некоторый код:

class Person(
    val id:UUID,
    val aliases:List[String],
    val reports:List[Report]) {

  def addAlias(name:String) = new Person(id,name :: aliases,reports)
}

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

Аналогично, я мог бы:

class Report(val id:UUID, val content:String) {
  /** Adding more info to our report */
  def updateContent(newContent:String) = new Report(id,newContent)
}

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

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

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

Ответ 1

Если X относится к Y, обе они неизменяемы, а Y изменяется (т.е. вы заменяете его обновленной копией), тогда у вас нет выбора, кроме как заменить X (потому что он изменился, поскольку новый X указывает на новый Y, а не старый).

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

  • Забудьте об неизменности в целом. Сделать ссылки изменчивыми. Закрепите их по мере необходимости. Убедитесь, что вы действительно исправляете их, или вы можете получить утечку памяти (X относится к старому Y, который относится к старому X, который относится к старшему Y и т.д.).
  • Не храните прямые ссылки, а скорее ID-коды, которые вы можете искать (например, ключ в хэш-карту). Затем вам необходимо обработать случай сбоя поиска, но в остальном ситуация довольно надежная. Это, конечно, немного медленнее, чем прямая связь.
  • Измените весь мир. Если что-то изменилось, все, что связано с ним, также должно быть изменено (и выполнение этой операции одновременно в сложном наборе данных является сложным, но теоретически возможным, или, по крайней мере, изменчивые аспекты его могут быть скрыты, например, с большим количеством ленивых векселей).

Что предпочтительнее зависит от скорости поиска и обновлений, я ожидаю.

Ответ 2

Я предлагаю вам прочитать, как люди справляются с проблемой в clojure и Akka. Читайте о Программная память транзакций. И некоторые из моих мыслей...

Неизменность существует не ради самого себя. Неизменность - это абстракция. Он не "существует" в природе. Мир изменчив, мир постоянно меняется. Поэтому вполне естественно, что структуры данных могут быть изменчивыми - они описывают состояние реального или имитируемого объекта в данный момент времени. И здесь похоже на ООП. На концептуальном уровне проблема с этим отношением заключается в том, что объект в RAM!= Реальный объект - данные могут быть неточными, он приходит с задержкой и т.д.

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

  • структуры данных модифицируются из параллельных потоков
  • пользователи предоставляют определенные изменения для тех же объектов
  • пользователь предоставляет недопустимые данные и должен быть откат

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

Ответ 3

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

Возможно ли, чтобы неизменяемый Человек имел ссылку на изменяемый PersonRecord, который хранит такие вещи, как отчеты и псевдонимы?