Неизменность и общие ссылки - как примирить? - PullRequest
10 голосов
/ 09 апреля 2010

Рассмотрим этот упрощенный домен приложения:

  • База данных уголовного розыска
  • 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 с новым псевдонимом становится новым объектом, также неизменным. Если 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, все источники будут видеть новое значение, ничего не делая.

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

Ответы [ 3 ]

10 голосов
/ 09 апреля 2010

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

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

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

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

5 голосов
/ 10 апреля 2010

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

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

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

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

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

3 голосов
/ 09 апреля 2010

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...