Come si decide se un tipo di oggetto dati debba essere progettato per essere immutabile?

23

Adoro il modello "immutabile" per via dei suoi punti di forza, e in passato ho trovato utile progettare sistemi con tipi di dati immutabili (alcuni, la maggior parte o anche tutti). Spesso quando lo faccio, mi ritrovo a scrivere meno bug e il debugging è molto più semplice.

Tuttavia, i miei coetanei in generale evitano immutabili. Non sono affatto inesperti (tutt'altro), ma scrivono gli oggetti dati in modo classico - membri privati con un getter e un setter per ogni membro. Quindi di solito i loro costruttori non accettano argomenti, o forse semplicemente prendono alcuni argomenti per convenienza. Così spesso, la creazione di un oggetto è simile a questa:

Foo a = new Foo();
a.setProperty1("asdf");
a.setProperty2("bcde");

Forse lo fanno ovunque. Forse non definiscono nemmeno un costruttore che prende quelle due stringhe, non importa quanto siano importanti. E forse non cambiano il valore di quelle stringhe più tardi e non ne hanno mai bisogno. Chiaramente se quelle cose sono vere, l'oggetto sarebbe meglio progettato come immutabile, giusto? (il costruttore prende le due proprietà, nessun setter)

Come decidi se un tipo di oggetto deve essere progettato come immutabile? esiste un buon insieme di criteri per giudicarlo?

Attualmente sto discutendo se convertire alcuni tipi di dati nel mio progetto in immutabili, ma dovrei giustificarlo con i miei colleghi, e i dati nei tipi potrebbero cambiare (MOLTO raramente) - a quel punto è possibile ovviamente cambialo in modo immutabile (creane uno nuovo, copiando le proprietà dal vecchio oggetto eccetto quelle che vuoi cambiare). Ma non sono sicuro se questo è solo il mio amore per i risultati immutabili, o se c'è un reale bisogno / beneficio da loro.

    
posta Ricket 28.02.2012 - 16:05
fonte

8 risposte

27

Sembra che ti stai avvicinando all'indietro. Uno dovrebbe essere immutabile. Crea un oggetto mutabile solo se devi / non riesci a farlo funzionare come un oggetto immutabile.

    
risposta data 28.02.2012 - 16:09
fonte
11

Il vantaggio principale degli oggetti immutabili è garantire la sicurezza del filo. In un mondo in cui più core e thread sono la norma, questo vantaggio è diventato molto importante.

Ma usare oggetti mutabili è molto conveniente. Funzionano bene, e fino a quando non li stai modificando da thread separati, e hai una buona comprensione di ciò che stai facendo, sono abbastanza affidabili.

    
risposta data 28.02.2012 - 16:28
fonte
6

Ci sono due modi principali per decidere se un oggetto è immutabile.

a) In base alla natura dell'Oggetto

È facile cogliere queste situazioni perché sappiamo che questi oggetti non cambieranno dopo la costruzione. Ad esempio, se hai un'entità RequestHistory e le entità della cronologia della natura non cambiano una volta costruita. Questi oggetti possono essere progettati direttamente come classi immutabili. Tieni presente che l'oggetto della richiesta è modificabile in quanto può cambiare il suo stato e chi è assegnato a ecc. Nel tempo, ma la cronologia delle richieste non cambia. Ad esempio, la scorsa settimana è stato creato un elemento cronologia quando è passato dallo stato inviato allo stato assegnato E questa storia non può mai cambiare. Quindi questo è un classico caso immutabile.

b) In base alla scelta progettuale, fattori esterni

Questo è simile all'esempio java.lang.String. Le stringhe possono effettivamente cambiare nel tempo, ma in base alla progettazione, l'hanno resa immutabile a causa del caching / pool di stringhe / fattori di concorrenza. Similare la memorizzazione nella cache / la concorrenza ecc. Può svolgere un ruolo importante nel rendere un oggetto immutabile se la caching / concorrenza e le prestazioni correlate sono vitali nell'applicazione. Ma questa decisione dovrebbe essere presa molto attentamente dopo aver analizzato tutti gli impatti.

Il vantaggio principale degli oggetti immutabili è che non sono soggetti a pattern di tumble-weed.i.e l'oggetto non rileva alcun cambiamento nel corso della vita e rende molto più semplice la codifica e la manutenzione.

    
risposta data 28.02.2012 - 16:40
fonte
4

I currently am debating whether to switch a few data types in my own project to immutable, but I would have to justify it to my peers, and the data in the types might (VERY rarely) change - at which time you can of course change it the immutable way (create a new one, copying the properties from the old object except for the ones that you want to change).

Ridurre al minimo lo stato di un programma è estremamente vantaggioso.

Chiedi loro se desiderano utilizzare un tipo di valore mutabile in una delle tue classi per l'archiviazione temporanea da una classe client.

Se dicono di sì, chiedi perché? Lo stato mutabile non appartiene a uno scenario come questo. Costringendoli a creare lo stato in cui effettivamente appartiene e rendendo lo stato dei tipi di dati il più esplicito possibile sono ottimi motivi.

    
risposta data 28.02.2012 - 18:06
fonte
4

La risposta è in qualche modo dipendente dalla lingua. Il tuo codice è simile a Java, dove questo problema è il più difficile possibile. In Java, gli oggetti possono essere passati solo per riferimento e il clone è completamente infranto.

Non esiste una risposta semplice, ma per alcuni si desidera rendere immutabili gli oggetti di valore small-ish. Java ha corretto le stringhe in modo immutabile, ma ha modificato la data e il calendario in modo errato.

Quindi, rendere definitivamente immutabili gli oggetti di piccolo valore e implementare un costruttore di copie. Dimentica tutto di Cloneable, è progettato così male da essere inutile.

Per oggetti di valore più grande, se è scomodo renderli immutabili, rendili facili da copiare.

    
risposta data 28.02.2012 - 18:05
fonte
1

And maybe they don't change the value of those strings later and never need to. Clearly if those things are true, the object would be better designed as immutable, right?

In qualche modo contro-intuitivamente, non è mai necessario cambiare le stringhe in seguito è un argomento abbastanza buono che non importa se gli oggetti sono immutabili o meno. Il programmatore li sta già trattando come effettivamente immutabile indipendentemente dal fatto che il compilatore lo implementi o meno.

L'immutabilità di solito non fa male, ma non sempre aiuta neanche. Il modo più semplice per capire se il tuo oggetto potrebbe trarre vantaggio dall'immutabilità è se hai bisogno di creare una copia dell'oggetto o acquisire un mutex prima di cambiarlo . Se non cambia mai, allora l'immutabilità non ti compra davvero nulla e talvolta rende le cose più complicate.

Tu fai hai un buon punto sul rischio di costruire un oggetto in uno stato non valido, ma questo è davvero un problema separato dall'immutabilità. Un oggetto può essere mutabile e sempre in uno stato valido dopo la costruzione.

L'eccezione a questa regola è che dal momento che Java non supporta né parametri nominali né parametri predefiniti, a volte può risultare poco pratico progettare una classe che garantisce un oggetto valido usando solo costruttori sovraccaricati. Non così tanto con il caso delle due proprietà, ma c'è anche qualcosa da dire per coerenza, se questo modello è frequente con classi simili ma più grandi in altre parti del codice.

    
risposta data 13.03.2014 - 23:08
fonte
0

Potrei avere una visione troppo bassa di questo e probabilmente perché sto usando C e C ++ che non rendono esattamente così semplice rendere tutto immutabile, ma vedo i tipi di dati immutabili come un ottimizzazione dettagli per scrivere funzioni più efficienti privo di effetti collaterali ed essere in grado di fornire facilmente funzionalità come i sistemi di annullamento e l'editing non distruttivo.

Ad esempio, questo potrebbe essere estremamente costoso:

/// @return A new mesh whose vertices have been transformed
/// by the specified transformation matrix.
Mesh transform(Mesh mesh, Matrix4f matrix);

... se Mesh non è progettato per essere una struttura di dati persistente ed era, invece, un tipo di dati che richiedeva di copiarlo completamente (che potrebbe estendersi in gigabyte in alcuni scenari) anche se tutto ciò che stiamo andando sta facendo sta cambiando una parte di esso (come nello scenario precedente in cui modifichiamo solo le posizioni dei vertici).

Ecco quindi quando raggiungo l'immutabilità e progetto la struttura dei dati per consentire che porzioni non modificate siano copiate e conteggio dei riferimenti superficiali, per consentire alla funzione sopra riportata di essere ragionevolmente efficiente senza dover copiare in profondità intere maglie mentre si è ancora in grado scrivere la funzione per essere priva di effetti collaterali che semplifica drasticamente la sicurezza del thread, la sicurezza delle eccezioni, la possibilità di annullare l'operazione, applicarla in modo non distruttivo, ecc.

Nel mio caso è troppo costoso (almeno dal punto di vista della produttività) per rendere tutto immutabile, quindi lo salvo per le classi che sono troppo costose da copiare in profondità. Queste classi sono solitamente strutture di dati pesanti come maglie e immagini e generalmente uso un'interfaccia mutevole per esprimere le modifiche ad esse attraverso un oggetto "costruttore" per ottenere una nuova copia immutabile. E non lo sto facendo così tanto per cercare di ottenere garanzie immutabili al livello centrale della classe tanto quanto aiutarmi ad usare la classe in funzioni che possono essere prive di effetti collaterali. Il mio desiderio di rendere Mesh sopra immutabile non è direttamente quello di rendere le mesh immutabili, ma di consentire facilmente la scrittura di funzioni prive di effetti collaterali che inseriscono una mesh e ne generano una nuova senza pagare una memoria enorme e costi computazionali in cambio.

Di conseguenza ho solo 4 tipi di dati immutabili nella mia intera base di codice, e sono tutte strutture di dati pesanti, ma li uso pesantemente per aiutarmi a scrivere funzioni prive di effetti collaterali. Questa risposta potrebbe essere applicabile se, come me, stai lavorando in una lingua che non rende così facile rendere tutto immutabile. In tal caso, è possibile spostare l'attenzione verso il fatto che la maggior parte delle funzioni eviti gli effetti collaterali, a quel punto si potrebbe voler rendere selezionabili strutture dati immutabili, come i dettagli di ottimizzazione per evitare le costose copie complete. Nel frattempo quando ho una funzione come questa:

/// @return The v1 * v2.
Vector3f vec_mul(Vector3f v1, Vector3f v2);

... quindi non ho alcuna tentazione di rendere i vettori immutabili dal momento che sono abbastanza economici da essere copiati per intero. Non c'è alcun vantaggio in termini di prestazioni da ottenere qui trasformando i vettori in strutture immutabili che possono copiare in profondità parti non modificate. Tali costi supererebbero il costo della semplice copia dell'intero vettore.

    
risposta data 17.12.2017 - 13:19
fonte
-1
Foo a = new Foo();
a.setProperty1("asdf");
a.setProperty2("bcde");

Se Foo ha solo due proprietà, allora è facile scrivere un costruttore che accetta due argomenti. Ma supponiamo che tu aggiunga un'altra proprietà. Quindi devi aggiungere un altro costruttore:

public Foo(String a, String b, SomethingElse c)

A questo punto, è ancora gestibile. Ma cosa succede se ci sono 10 proprietà? Non vuoi un costruttore con 10 argomenti. È possibile utilizzare il modello di builder per costruire istanze, ma ciò aggiunge complessità. A questo punto, starai pensando "perché non ho solo aggiunto setter per tutte le proprietà come fanno le persone normali?"

    
risposta data 28.02.2012 - 17:37
fonte

Leggi altre domande sui tag