2010-04-08 20 views
10

Considerate questo dominio applicazione semplificata:Immutabilità e riferimenti condivisi: come conciliare?

  • penale banca dati Investigative
  • Person è chiunque sia coinvolto in un'indagine
  • Report è un po 'di informazioni che fa parte di un'indagine
  • A Report riferimenti a primarie Person (oggetto di un'indagine)
  • A Report ha complici secondariamente correlati (e potrebbe c essere primariamente in altre indagini o rapporti
  • Queste classi hanno id che vengono utilizzati per memorizzarli in un database, poiché le loro informazioni possono cambiare nel tempo (ad es. potremmo trovare nuovi alias per una persona, o aggiungere persone di interesse per un rapporto)

Domain http://yuml.me/13fc6da0

Se questi sono memorizzati in una sorta di banca dati e desidero utilizzare oggetti immutabili, non sembra essere un problema per quanto riguarda lo stato e il riferimento.

Supponendo di modificare alcuni metadati su uno Person. Dal momento che i miei Person oggetti immutabili, potrei avere un po 'di codice come:

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

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

Così che il mio Person con un nuovo alias diventa un nuovo oggetto, anche immutabile. Se a Report si riferisce a quella persona, ma l'alias è stato modificato altrove nel sistema, il mio Report ora si riferisce alla persona "vecchia", cioè la persona senza il nuovo alias.

Allo stesso modo, potrei avere:

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

Dal momento che questi oggetti non so chi vi fa riferimento, non è chiaro per me come lasciare tutti i "referenti" sanno che c'è un nuovo oggetto disponibile che rappresenta lo stato più recente.

Questo può essere fatto facendo in modo che tutti gli oggetti "si aggiornino" da un archivio dati centrale e tutte le operazioni che creano nuovi oggetti aggiornati vengano archiviati nell'archivio dati centrale, ma si tratta di una reimplementazione approssimativa del riferimento della lingua sottostante. cioè sarebbe più semplice rendere questi "oggetti memorizzabili secondari" semplicemente mutabili. Quindi, se aggiungo un alias a Person, tutti i referrer visualizzano il nuovo valore senza fare nulla.

Come viene gestita questa funzione quando si desidera evitare la mutabilità o si tratta di un caso in cui l'immutabilità non è utile?

+1

È possibile implementare l'elenco collegato doppio in haskell. http://www.haskell.org/haskellwiki/Tying_the_Knot Se capisci questo ed è applicabile al tuo problema, sarebbe bello se rispondessi. :-) –

+0

@Thomas Jung: Penso che la soluzione sopra si avvantaggi di alcune stranezze che si ottengono con la valutazione pigra. Scala, essendo attentamente valutato, ha bisogno di saltare attraverso i cerchi per ottenere lo stesso effetto. Penso che la soluzione più semplice qui sia semplicemente quella di evitare le dipendenze cicliche con strutture di dati immutabili. – Juliet

risposta

10

Se X si riferisce a Y, entrambi sono immutabili e Y cambia (ovvero lo sostituisci con una copia aggiornata), quindi non hai altra scelta che sostituire X anche (perché è cambiato, poiché la nuova X punta a la nuova Y, non quella vecchia).

Questo diventa rapidamente un mal di testa da mantenere in strutture di dati altamente interconnesse. Hai tre approcci generali.

  • Dimenticate l'immutabilità in generale. Rendi mutabili i collegamenti. Correggili quando necessario.Assicurati di risolverli davvero, o potresti avere una perdita di memoria (X si riferisce alla vecchia Y, che si riferisce alla vecchia X, che si riferisce alla vecchia Y, ecc.).
  • Non memorizzare i collegamenti diretti, ma piuttosto i codici ID che è possibile cercare (ad esempio una chiave in una mappa hash). È quindi necessario gestire il caso di errore di ricerca, ma per il resto le cose sono abbastanza robuste. Questo è un po 'più lento del collegamento diretto, ovviamente.
  • Cambia tutto il mondo. Se qualcosa è cambiato, anche tutto ciò che si collega ad esso deve essere cambiato (e l'esecuzione simultanea di questa operazione su un insieme di dati complessi è complicata, ma teoricamente possibile, o almeno gli aspetti mutabili di essa possono essere nascosti ad esempio con un sacco di valori pigri) .

Quale è preferibile dipende dalla percentuale di ricerche e aggiornamenti, mi aspetto.

+1

nella maggior parte dei casi la seconda opzione è la migliore (usa ID come link, non come indirizzo) – Javier

+0

@ Javier Agreed. –

+0

L'opzione 2 non sta replicando ciò che il runtime della lingua sta già facendo tramite i riferimenti agli oggetti? Cosa si ottiene da questo? Il runtime della lingua probabilmente implementa meglio la gestione ref di me. – davetron5000

3

Penso che stiate provando a quadrare il cerchio. La persona è immutabile, l'elenco dei rapporti su una persona fa parte della persona e l'elenco dei rapporti può cambiare.

Sarebbe possibile che una Persona immutabile abbia un riferimento a un PersonRecord mutabile che conserva cose come Rapporti e Alias?

+0

1) La persona non è necessaria immutabile - ad es. il nome di una persona può cambiare e l'applicazione deve gestirlo in qualche modo. 2) non ha senso riferirsi a oggetti mutabili da immutabili. il vantaggio principale dell'oggetto immutabile è la trasparenza referenziale delle espressioni che contengono pure funzioni e oggetti immutabili. E.I. tali espressioni restituiscono sempre lo stesso valore, non producono alcun cambiamento di stato e non dipendono da alcun cambiamento di stato. Li rende affidabili e thread-safe. Quando rimandi l'oggetto mutabile da immutabile non è più trasparente referenziale. Quindi - no point – Alexey

+0

Se (1) è il caso, il suo problema va via. Quello che dici sotto (2) è convincente TRANNE prendere in considerazione l'altra soluzione proposta: "Non memorizzare link diretti, ma piuttosto ID". In tal caso, un Report non punta a una Persona ma fa riferimento a Persona 341, per la quale esiste una PersonDescription attualmente valida e immutabile. Accettabile, ma se usi il rapporto apparentemente immutabile, è possibile che venga generata una nuova PersonaDescrizione e che la trasparenza referenziale scompaia. Forse un modello transazionale sarebbe migliore, ma anche questo ha i suoi problemi. – Malvolio

5

Ti suggerisco di leggere in che modo le persone affrontano il problema in clojure e Akka. Maggiori informazioni su Software transactional memory. E alcuni dei miei pensieri ...

L'immutabilità non esiste per il gusto di se stessa. L'immutabilità è l'astrazione. Non "esiste" in natura. Il mondo è mutevole, il mondo sta cambiando in modo permanente. Quindi è abbastanza naturale che le strutture dati siano mutabili: descrivono lo stato dell'oggetto reale o simulato in un dato momento nel tempo. E sembra come il regola OOP qui. A livello concettuale il problema con questa attitudine è quell'oggetto in RAM! = Oggetto reale - i dati possono essere inaccurati, con ritardo ecc.

Quindi in caso di requisiti più banali si può andare con tutto mutabile - persone, rapporti ecc problemi pratici sorgeranno quando:

  1. strutture di dati sono modificate dal thread concorrenti
  2. utenti forniscono conficting modifiche per gli stessi oggetti
  3. un utente fornisce un dati non validi e dovrebbe essere riportato indietro

Con il modello mutevole ingenuo si finisce rapidamente con dati incoerenti e sistema di frantumazione. La mutabilità è soggetta a errori, l'immutabilità è impossibile. Ciò di cui hai bisogno è la visione transazionale del mondo. All'interno del programma di transazione si vede il mondo immutabile. E STM gestisce le modifiche da applicare in modo coerente e sicuro per i thread.