Le persone del database lo chiamano Normalizzazione . I buoni progetti di sistema tendono a iniziare solo con dati normalizzati e fanno solo eccezioni, se necessario.
Un contro-esempio non menzionato nell'articolo di normalizzazione è la programmazione concorrente. Quando due o più processi accedono allo stesso pezzo di dati mutabili, ci sono tutti i tipi di problemi in cui il processo A
inizia una lettura, quindi il processo B
esegue una scrittura, quale valore processa A
letto? Quello vecchio o quello nuovo? Se i tuoi dati sono un oggetto complesso, A
può ottenere un puntatore a un nuovo oggetto, ma non inizializzato e iniziare a usarlo prima che sia completamente creato.
Quando devi condividere dati mutabili tra i processi, spesso è meglio rendere copie difensive dei tuoi dati. In questo modo process A
può garantire completamente che il processo B
legga un valore corretto, completamente inizializzato e che il processo B
non possa quindi modificare accidentalmente o intenzionalmente il valore dei dati di A
(o viceversa).
Questa è la ragione per cui Java ha la convenzione di usare quei fastidiosi metodi get / set. Se esponi un campo con un get / set, successivamente scopri un problema in cui un client della tua classe sta cambiando il comportamento sottostante della tua classe in un modo non sicuro, quindi senza modificare l'interfaccia della tua classe puoi:
- Rendi immutabili i dati sottostanti
- Restituisce una copia difensiva dal metodo get ()
- Aggiungi la sincronizzazione ai metodi get / set
I dati immutabili sono la soluzione più semplice e affidabile quando è pratico progettare la classe in modo che i dati in essa contenuti non cambino. Se l'oggetto ha un piccolo ingombro di memoria e non verrà creato così tante volte da riempire la memoria con piccole copie di se stesso, allora una copia difensiva è probabilmente più efficiente della sincronizzazione. Se l'oggetto sottostante è costoso da creare, ha un ingombro di memoria di grandi dimensioni, o deve essere creato così tante volte da riempire la memoria rapidamente, quindi la sincronizzazione potrebbe essere migliore delle copie difensive.
Linguaggi più recenti come Ruby e Scala generano metodi get / set impliciti per te in modo tale che sembra che tu stia accedendo direttamente ai campi, ma puoi in seguito sovrascrivere i metodi get / set predefiniti come descritto sopra. I linguaggi funzionali come Scala, Clojure o Haskell presuppongono che tutti i dati siano immutabili, a meno che non si specifichi diversamente.
Anche all'interno di un singolo processo è molto semplice passare myObject a diversi metodi o usarlo su diverse pagine di codice all'interno della stessa procedura e in un posto si imposta myObject.color = BLUE e da qualche altra parte myObject.color = RED e ogni posto si aspetta che myObject mantenga il suo colore. È facile fare questo tipo di errore di programmazione ogni volta che il codice diventa abbastanza complicato.