Cambiare oggetti immutabili come funziona?

3

Ho visto esempi in cui, anche se l'oggetto è immutabile, ci sono situazioni in cui è necessario aggiornare alcuni campi e, a quanto ho capito, l'oggetto viene passato a un costruttore che esegue una copia con valori aggiornati ma l'originale non è cambiato. Bene, ma cosa succede se due thread vogliono farlo? Non incontriamo problemi di sincronizzazione se entrambi stanno cambiando lo stesso campo? E come se ora ci sono nuovi oggetti realizzati (piuttosto che un originale modificato) fanno più thread sapere che i valori dei campi sono cambiati?

Ho visto molti esempi su come creare una classe immutabile, ma non mi è chiaro come gli attributi di aggiornamento funzionino in un ambiente multithread, come la sincronizzazione non sia richiesta e come ho detto sopra, come determinare quale oggetto rappresenti ora il stato attuale.

Ecco un articolo in cui viene discussa la modifica di un oggetto immutabile: link

    
posta Jeff 15.06.2018 - 20:43
fonte

2 risposte

10

Fine, but what if two threads want to do this?

Quindi un thread esegue una modifica (e ottiene una versione dell'oggetto) e un altro thread effettua un'altra modifica (e ottiene una versione diversa e indipendente dell'oggetto).

Do we not run into synchronization problems if they both are changing the same field?

No, perché ogni thread ottiene la propria copia dell'oggetto.

And how if now there are new objects made (rather than a changed original) do multiple threads know that the values of fields have changed?

Non lo fanno. Questo è il punto.

how to determine what object now represents the current state.

E questo è il trucco. L'immutabilità funziona alla grande quando le cose sono valori o quando le cose sono veramente indipendenti. Ma se si dispone di un oggetto che rappresenta un singolo record, ad esempio da un database, è necessario unire le modifiche in quel singolo record. Questo può essere l'ultimo nelle vittorie. Questo può essere un modo di fare il versioning / timestamping. Ci sono molte opzioni con vari compromessi.

Ma il bit chiave è che la sincronizzazione deve avvenire solo nell'ultima fase in cui le modifiche vengono unite e hanno effettivamente effetto sulla singola fonte di verità.

    
risposta data 15.06.2018 - 21:06
fonte
2

Il articolo che hai linkato parla dell'utilizzo di Builder Pattern per creare un oggetto. Non c'è alcuna differenza concettuale tra farlo e usare un costruttore. Tratta semplicemente tutti i passaggi del Builder come una singola operazione.

Durante il processo di costruzione, non stai mutando un oggetto immutabile, perché l'oggetto non è considerato completamente costruito fino a quando non chiami Build() sul builder, a quel punto avrai quindi un oggetto immutabile completamente costruito.

Questo vale anche per i costruttori. Molti costruttori hanno diversi passaggi all'interno del corpo del metodo costruttore. Tutti questi passaggi devono essere completati prima che l'oggetto sia considerato completamente costruito e ne consegua un oggetto immutabile utilizzabile.

Se vuoi rendere un nuovo oggetto immutabile da un oggetto immutabile esistente con alcuni dei valori modificati, ciò di cui ora hai bisogno è un nuovo costruttore o costruttore che prende un oggetto esistente come parametro e quindi imposta tutti gli attributi nel costruttore o nel costruttore che si desidera modificare.

Ad esempio:

public BookBuilder(Book book) {
    this.isbn = book.getIsbn();
    this.publicationYear = book.getPublicationYear();
    this.reviews = book.getReviews();
}

E poi

Book originalBook = getRandomBook();

Book modifiedBook = new 
BookBuilder(originalBook).isbn("123456").publicationYear(2011).build();

Il builder crea una copia del libro originale, chiama i metodi nelle tue interfacce fluenti per impostare i campi desiderati su nuovi valori, quindi il metodo build() blocca il nuovo oggetto in qualche modo in modo che diventi immutabile.

Nota che l'autore fa due concessioni:

  1. Il builder è non thread safe, e
  2. Devi ancora fare copie di oggetti.

Quando usi i costruttori, puoi impostare e modificare final o readonly membri finché il costruttore termina l'esecuzione, a quel punto questi membri diventano non modificabili.

    
risposta data 15.06.2018 - 23:11
fonte

Leggi altre domande sui tag