Non puoi scrivere un buon codice senza getter.
Il motivo per cui non è perché i getter non rompono l'incapsulamento, lo fanno. Non è perché i getter non tentano le persone a non preoccuparsi di seguire OOP, il che indurrebbe loro a mettere metodi con i dati su cui agiscono. Loro fanno. No, hai bisogno di getter a causa dei confini.
Le idee di incapsulamento e mantenimento dei metodi insieme ai dati su cui agiscono semplicemente non funzionano quando ci si imbatte in un limite che ti impedisce di spostare un metodo e quindi ti costringe a spostare i dati.
È davvero così semplice. Se usi i getter quando non c'è limite, finisci per non avere oggetti reali. Tutto inizia a tendere alla procedura. Che funziona come non mai.
True OOP non è qualcosa che puoi diffondere ovunque. Funziona solo entro quei confini.
Quei confini non sono sottili. Hanno un codice al loro interno. Quel codice non può essere OOP. Non può nemmeno essere funzionale. No, a questo codice sono stati sottratti i nostri ideali in modo da poter gestire la dura realtà.
Michael Fetters chiamò questa fascia di codice dopo quel tessuto connettivo bianco che tiene insieme le sezioni di un'arancia.
Questo è un modo meraviglioso di pensarci. Spiega perché è giusto avere entrambi i tipi di codice nella stessa base di codice. Senza questa prospettiva molti nuovi programmatori si aggrappano duramente ai loro ideali, quindi si spezzano il cuore e rinunciano a questi ideali quando colpiscono il loro primo confine.
Gli ideali funzionano solo al posto giusto. Non rinunciare a loro solo perché non funzionano ovunque. Usali dove lavorano. Quel posto è la parte succosa che protegge la fascia.
Un semplice esempio di confine è una raccolta. Questo contiene qualcosa e non ha idea di cosa sia. In che modo un designer di collezioni potrebbe spostare la funzionalità comportamentale dell'oggetto trattenuto nella collezione quando non ha idea di cosa avrà in mano? Non puoi. Sei contro un confine. Ecco perché le collezioni hanno getter.
Ora, se lo sapessi, potresti spostare quel comportamento ed evitare di cambiare stato. Quando lo sai, dovresti. Semplicemente non sempre lo sai.
Alcune persone lo chiamano solo pragmatico. E questo è. Ma è bello sapere perché dobbiamo essere pragmatici.
Hai espresso che non vuoi ascoltare argomenti semantici e sembra che tu stia promuovendo ovunque "getter sensati". Stai chiedendo di sfidare questa idea. Penso di poter mostrare che l'idea ha problemi con il modo in cui l'hai inquadrata. Ma pensa anche che io sappia da dove vieni perché sono stato lì.
Se vuoi getter ovunque, guarda Python. Non esiste una parola chiave privata. Eppure Python fa OOP bene. Come? Usano un trucco semantico. Nominano qualsiasi cosa intenda essere privata con un trattino basso. Puoi anche leggere da esso, a condizione che ti assuma la responsabilità di farlo. "Siamo tutti adulti qui", dicono spesso.
Quindi qual è la differenza tra questo e mettere semplicemente i getter su tutto in Java o C #? Scusa ma è semantica. La convenzione di sottolineatura di Pythons indica chiaramente che stai frugando dietro l'unica porta dei dipendenti. Schiaffeggia su tutto e perdi quel segnale. Con la riflessione avresti comunque potuto spogliare il privato e non aver perso il segnale semantico. Semplicemente non c'è un argomento strutturale da formulare qui.
Quindi ciò che ci resta è il compito di decidere dove appendere il cartello "solo dipendenti". Cosa dovrebbe essere considerato privato? Tu lo chiami "getter sensibili". Come ho detto, la migliore giustificazione per un getter è un confine che ci allontana dai nostri ideali. Ciò non dovrebbe tradursi in vincitori di tutto. Quando si traduce in un getter, dovresti considerare di spostare ulteriormente il comportamento nella parte succosa dove puoi proteggerlo.
Questa separazione ha dato origine a pochi termini. Un Data Transfer Object o DTO non ha alcun comportamento. Gli unici metodi sono getter e talvolta setter, a volte un costruttore. Questo nome è sfortunato perché non è affatto un vero oggetto. Getter e setter sono in realtà solo codici di debug che ti danno un posto per impostare un breakpoint. Se non fosse stato per quel bisogno, sarebbero solo un mucchio di campi pubblici. In C ++ li chiamavamo strutture. L'unica differenza che avevano da una classe C ++ era quella di essere resi pubblici.
I DTO sono carini perché puoi gettarli oltre un muro di cinta e mantenere gli altri metodi in modo sicuro in un oggetto di comportamento succoso e piacevole. Un vero oggetto. Senza vincitori violare l'incapsulamento. I miei oggetti comportamentali possono mangiare DTO usandoli come oggetti parametro . A volte devo crearne una copia difensiva per prevenire uno stato mutevole condiviso . Non diffondo DTO mutevoli all'interno della parte succosa all'interno del confine. Li incapsulo. Li nascondo. E quando finalmente mi imbatto in un nuovo confine, lancio un nuovo DTO e lo lancio sul muro rendendolo così il problema di qualcun altro.
Ma vuoi fornire a coloro che esprimono identità. Beh, complimenti, hai trovato una lavanderia. Le entità hanno un'identità che va oltre il loro riferimento. Cioè, oltre il loro indirizzo di memoria. Quindi deve essere conservato da qualche parte. E qualcosa deve essere in grado di riferirsi a questa cosa dalla sua identità. Un getter che esprime identità è perfettamente ragionevole. Un mucchio di codice che usa quel getter per prendere decisioni che l'Entità non avrebbe potuto prendere.
Alla fine non è l'esistenza di getter che è sbagliata. Sono molto meglio dei campi pubblici. La cosa brutta è quando sono abituati a fingere di essere orientati agli oggetti quando non lo sei. Le migliori sono buone. Essere orientati agli oggetti è buono. I getter non sono orientati agli oggetti. Usa i getter per ritagliarti un posto sicuro in cui orientarti agli oggetti.