Рассмотрите этот упрощенный домен приложения:
Person
любой вовлеченный в расследованиеReport
немного информации, которая является частью расследованияReport
ссылается на основное устройство Person
(предмет расследования)Report
имеет сообщников, которые во вторую очередь связаны (и могло, конечно, быть основным в других расследованиях или отчетахДоменный http://yuml.me/13fc6da0
Если они хранятся в своего рода базе данных, и я хочу использовать неизменные объекты, кажется, существует проблема относительно состояния и ссылки.
Если я изменяю некоторые метаданные о a 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)
}
Так как эти объекты не знают, кто обращается к ним, мне не ясно, как позволить всем "ссылающимся доменам" знать, что существует новое объектное доступное представление нового состояния.
Это могло быть сделано при наличии всех объектов "обновление" от центрального хранилища данных и всех операций, которые создают новый, обновленный, хранилище объектов к центральному хранилищу данных, но это чувствует себя подобно дрянному переопределению ссылки базового языка. т.е. это было бы более ясно просто сделать эти "вторичные storable объекты" изменяемыми. Так, если я добавляю псевдоним к a Person
, все ссылающиеся домены видят новое значение, ничего не делая.
Как с этим имеют дело с тем, когда мы хотим избежать переменчивости, или действительно ли это - случай, где неизменность не полезна?
Если X относится к Y, оба неизменяемы, а Y изменяется (т.е. вы заменяете его обновленной копией), тогда у вас нет другого выбора, кроме как заменить X также (потому что он изменился, поскольку новый X указывает на новый Y, а не на старый).
Это быстро становится головной болью при поддержке сильно взаимосвязанных структур данных. У вас есть три общих подхода.
Я полагаю, что предпочтительнее, зависит от вашей скорости поиска и обновлений.
Я думаю, вы пытаетесь квадрат круга. Person является неизменным, список отчетов о человеке является частью Person, и список отчетов может изменяться.
Может ли неизменяемый объект Person иметь ссылку на изменяемый объект PersonRecord, в котором хранятся такие вещи, как отчеты и псевдонимы?
Я предлагаю вам прочитать, как люди решают эту проблему в clojure и Akka. Почитайте о программной транзакционной памяти. И некоторые мои мысли...
Неизменность существует не ради самой себя. Неизменность - это абстракция. Она не "существует" в природе. Мир изменчив, мир постоянно меняется. Поэтому вполне естественно, что структуры данных могут быть изменяемыми - они описывают состояние реального или моделируемого объекта в данный момент времени. И похоже, что ООП здесь рулит. На концептуальном уровне проблема с таким отношением заключается в том, что объект в оперативной памяти = реальный объект - данные могут быть неточными, они приходят с задержкой и т.д.
Поэтому в случае самых тривиальных требований вы можете обойтись всем мутабельным - лицами, отчетами и т.д. Практические проблемы возникнут, когда:
С наивной мутабельной моделью вы быстро закончите с несогласованными данными и разрушающейся системой. Мутабельность подвержена ошибкам, а неизменяемость невозможна. Вам нужен транзакционный взгляд на мир. В рамках транзакции программа видит неизменяемый мир. А STM управляет изменениями, чтобы применить их последовательным и потокобезопасным способом.