Vedo immutabili i vantaggi di rendere gli oggetti nel mio programma. Quando penso davvero a un buon design per la mia applicazione, spesso arrivo naturalmente a molti dei miei oggetti immutabili. Spesso arriva al punto in cui vorrei avere tutti i miei oggetti immutabili.
Questa domanda si occupa della stessa idea, ma nessuna risposta suggerisce quale sia un buon approccio all'immutabilità e quando utilizzarlo effettivamente. Ci sono alcuni buoni modelli di design immutabili? L'idea generale sembra essere "rendere gli oggetti immutabili a meno che tu non ne abbia assolutamente bisogno per cambiare" che è inutile nella pratica.
La mia esperienza è che l'immutabilità porta sempre più il mio codice al paradigma funzionale e questa progressione avviene sempre:
- Comincio a necessitare di strutture di dati persistenti (in senso funzionale) come elenchi, mappe, ecc.
- È estremamente scomodo lavorare con i riferimenti incrociati (ad esempio il nodo dell'albero che fa riferimento ai suoi figli mentre i bambini fanno riferimento ai loro genitori) che non mi consente affatto di utilizzare i riferimenti incrociati, il che rende le mie strutture di dati e il mio codice più funzionali.
- L'ereditarietà si interrompe per dare un senso e inizio a usare la composizione.
- Le idee di base di OOP come l'incapsulamento iniziano a sfaldarsi e i miei oggetti iniziano a sembrare funzioni.
A questo punto praticamente non sto più usando nulla del paradigma OOP e posso semplicemente passare a un linguaggio puramente funzionale. Quindi la mia domanda: esiste un approccio coerente al buon design OOP immutabile o è sempre vero che quando porti l'idea immutabile al suo massimo potenziale, finisci sempre per programmare in un linguaggio funzionale che non ha più bisogno del mondo OOP? Ci sono buone linee guida per decidere quali classi dovrebbero essere immutabili e quali dovrebbero rimanere mutabili per garantire che OOP non cada a pezzi?
Per comodità, fornirò un esempio. Facciamo ChessBoard
una collezione immutabile di pezzi degli scacchi immutabili (estendendo la classe astrattaPiece
). Dal punto di vista OOP, un pezzo è responsabile della generazione di mosse valide dalla sua posizione sul tabellone. Ma per generare le mosse il pezzo ha bisogno di un riferimento alla sua tavola mentre la tavola deve avere un riferimento ai suoi pezzi. Bene, ci sono alcuni trucchi per creare questi riferimenti incrociati immutabili a seconda del linguaggio OOP, ma sono difficili da gestire, meglio non avere un pezzo per fare riferimento alla sua scheda. Ma poi il pezzo non può generare le sue mosse poiché non conosce lo stato del tabellone. Quindi il pezzo diventa solo una struttura di dati che contiene il tipo di pezzo e la sua posizione. È quindi possibile utilizzare una funzione polimorfica per generare mosse per tutti i tipi di pezzi. Ciò è perfettamente realizzabile nella programmazione funzionale ma quasi impossibile in OOP senza controlli del tipo di runtime e altre pratiche OOP errate ... Quindi,