Qual è un problema preciso nel consentire i getter?


15

Non sto cercando un'opinione sulla semantica, ma semplicemente un caso in cui il fatto di avere un buon uso dei getter è un vero impedimento. Forse mi getta in una spirale infinita di affidamento su di loro, forse l'alternativa è più pulita e gestisce automaticamente i getter, ecc. Qualcosa di concreto.

Ho sentito tutti gli argomenti, ho sentito che sono cattivi perché ti costringono a trattare gli oggetti come fonti di dati, che violano lo "stato puro" di un oggetto di "non dare troppo ma essere pronto a accetta molto ".

Ma assolutamente nessuna ragione ragionevole per cui una getDataè una brutta cosa, in effetti, alcune persone hanno sostenuto che si tratta molto di semantica, di per sé come ottimi, ma non nominarli getX, per me, questo è almeno divertente .

Qual è una cosa, senza opinioni, che si romperà se uso i getter in modo ragionevole e per i dati che chiaramente l'integrità dell'oggetto non si rompe se lo mette in evidenza?

Ovviamente consentire un getter per una stringa che viene utilizzata per crittografare qualcosa non è affatto stupido, ma sto parlando di dati che il tuo sistema deve funzionare. Forse i tuoi dati vengono estratti Providerda a dall'oggetto, ma, comunque, l'oggetto deve ancora consentire Providera fare un $provider[$object]->getData, non c'è modo di aggirarlo.


Perché sto chiedendo: per me, i getter, se usati in modo ragionevole e sui dati considerati "sicuri" sono inviati da dio, il 99% dei miei getter sono utilizzati per identificare l'oggetto, come in, chiedo, tramite il codice Object, what is your name? Object, what is your identifier?, chiunque lavori con un oggetto dovrebbe sapere queste cose su un oggetto, perché quasi tutto ciò che riguarda la programmazione è identità e chi altro sa meglio di cosa si tratta rispetto all'oggetto stesso? Quindi non riesco a vedere alcun vero problema a meno che tu non sia un purista.

Ho esaminato tutte le domande StackOverflow sul "perché i getter / setter" sono cattivi e anche se concordo sul fatto che i setter sono davvero cattivi nel 99% dei casi, i getter non devono essere trattati allo stesso modo solo perché fanno rima.

Un setter comprometterà l'identità del tuo oggetto e renderà molto difficile il debug di chi sta cambiando i dati, ma un getter non sta facendo nulla.


2
Il mio consiglio è di dimenticare gran parte della teoria OO. Pratica. Scrivi molto codice e poi gestiscilo. Imparerai molto, molto, molto di più su ciò che funziona bene e cosa no dal fare effettivamente qualcosa e poi tornando ad esso qualche mese dopo.
jpmc26,

1
@ jpmc26 Cosa nella madre di .... In realtà non mi sono mai reso conto che nessuno di noi fa effettivamente OOP correttamente, ho sempre avuto questa nozione di incapsulamento strettamente correlata ai campi di un oggetto, ma l'oggetto stesso è uno stato e viene liberamente condiviso. Sinceramente tutti stanno sbagliando OOP, quindi, o meglio, OOP non è possibile se il paradigma è oggetti. Uso OOP rigorosamente per esprimere come funziona un sistema e non lo ho mai trattato come il Santo Graal, né SOLID. Sono solo strumenti che uso principalmente per definire un concetto ragionevolmente ben sviluppato (attraverso l'implementazione). Qualcuno legge se questo è giusto?
coolpasta,

2
--cont, Più scrivo, più mi rendo conto che la programmazione è tutto sulla capacità di astrarre quanto è necessario per poter ragionare, con gli altri, e anche con te sulla tua base di codice. Naturalmente, il lato tecnico di questo è assicurarsi di fare i controlli adeguati / impostare le regole giuste. In verità, chiunque riesce a far capire meglio agli altri il loro codice vince. Assicurati che abbia senso per chiunque lo legga, quindi assicurati che non fallisca segmentando correttamente gli oggetti con interfacce, controlli e passa ad altri paradigmi quando è più facile ragionarli: sii flessibile.
coolpasta,

2
Questo mi sembra un pagliaccio. Non credo che nessuno sviluppatore serio che scriva effettivamente codice utilizzabile sosterrebbe che l'uso dei getter in modo sensato è un problema. La domanda implica che i getter possono essere usati in modo sensato, il che implica che i getter possono essere sensati. Stai cercando qualcuno che dica che non c'è un uso ragionevole dei Getter? Aspetterai un po '.
JimmyJames

2
@coolpasta Hai ragione. Non mi interessa davvero se è OOP puro. Solo meglio. Dammi una scelta tra il codice non funzionante che può essere compreso dagli umani e il perfetto codice amante della CPU che è indecifrabile e che prenderò sempre il codice amico umano. OOP mi è utile SOLO quando riduce il numero di cose a cui devo pensare in qualsiasi momento. Questa è l'unica ragione per cui mi piace rispetto alle semplici procedure. Senza questo, mi stai facendo saltare attraverso più file di codice sorgente senza motivo.
candied_orange

Risposte:


22

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.


Questo è esattamente il motivo per cui ho chiesto e non sono sicuro che la mia conversazione sia visibile con Robert, stavo anche cercando di scoprire un caso specifico in cui mi fa chiaramente male usare "getter sensibili". Io stesso non sono d' accordo con i setter perché almeno per me stesso, è molto difficile attribuire la proprietà a un'azione come questa, usavo i setter in tutto e quando ho capito che anche 1 setter, quando è colpito da troppi oggetti, uccide io ... ho dovuto riscrivere tutto. L'uso ragionevole dei getter, dopo un test approfondito per così tanto tempo, sul serio non ha lati negativi se non sei un purista.
coolpasta,

1
@coolpasta è possibile creare oggetti proprietario dei dati. Gli oggetti proprietario dei dati sono oggetti dati, ma sono progettati e denominati in modo da possedere (chiaramente) i dati. Naturalmente questi oggetti avranno getter.
rwong

1
@rwong Cosa vuoi dire con clearly? Se i dati mi vengono trasmessi da qualcosa, per valore , chiaramente, il mio oggetto ora possiede i dati, ma per semantica, ma a questo punto non li ottiene semplicemente da qualcun altro. Quindi deve eseguire operazioni per trasformare quei dati, rendendoli propri, nel momento in cui i dati vengono toccati, è necessario apportare modifiche semantiche: i dati sono stati nominati come $data_from_requeste ora ci hai operato? Dillo $changed_data. Desideri esporre questi dati ad altri? Crea un getter getChangedRequestData, quindi la proprietà è chiaramente impostata. O è?
coolpasta,

1
"Non puoi scrivere un buon codice senza getter." Questo è assolutamente sbagliato, così come l'idea che i confini intrinsecamente necessitano di getter. Questo non è pragmatismo, solo pigrizia. È abbastanza più semplice semplicemente lanciare i dati oltre il recinto e finirlo, pensare ai casi d'uso e progettare una buona API comportamentale è difficile.
Robert Bräutigam,

2
Risposta eccellente!
cmaster - ripristina monica

10

Gli scommettitori violano il Principio di Hollywood ("Non chiamarci, ti chiameremo")

Il Principio di Hollywood (alias Inversion of Control) afferma che non si chiama il codice della biblioteca per fare le cose; piuttosto, il framework chiama il tuo codice. Poiché il framework controlla le cose, non è necessario trasmettere il proprio stato interno ai propri client. Non devi sapere.

Nella sua forma più insidiosa, la violazione del Principio di Hollywood significa che stai usando un getter per ottenere informazioni sullo stato di una classe e quindi prendere decisioni su quali metodi chiamare su quella classe in base al valore che ottieni. è la violazione dell'incapsulamento al suo meglio.

L'uso di un getter implica che hai bisogno di quel valore, quando in realtà non lo fai.

Potresti effettivamente aver bisogno di quel miglioramento delle prestazioni

In casi estremi di oggetti leggeri che devono avere le massime prestazioni possibili, è possibile (anche se estremamente improbabile) che non si possa pagare la penalità di prestazione molto piccola che un getter impone. Questo non accadrà il 99,9 percento delle volte.


1
Capisco e volerò con la tua verità, che non ho bisogno di sapere. . Ma ho raggiunto un punto in cui ho bisogno. Ho un Generatoroggetto che scorre attraverso tutti i miei Itemsoggetti, quindi chiama getNameda ciascuno Itemper fare qualcosa di più. Qual è il problema con questo? Quindi, in cambio, Generatorsputa le stringhe formattate. Questo è nel mio framework, per il quale ho quindi un'API in più che le persone possono usare per eseguire tutto ciò che gli utenti forniscono ma senza toccare il framework.
coolpasta,

3
What is the issue with this?Nessuno che io possa vedere. Questo è essenzialmente ciò che fa una mapfunzione. Ma questa non è la domanda che hai posto. In sostanza hai chiesto "Ci sono delle condizioni in cui un getter potrebbe essere sconsigliabile". Ho risposto con due, ma ciò non significa che abbandoni del tutto i setter.
Robert Harvey,

1
Stai facendo questa domanda al ragazzo sbagliato. Sono un pragmatico; Faccio tutto ciò che meglio si adatta ai miei programmi specifici e non metto molto in "principi" se non servono ai miei scopi.
Robert Harvey,

2
@ jpmc26: Frameworks. Non biblioteche.
Robert Harvey,

2
Un framework è un tentativo di trasformare una lingua per scopi generici in una lingua specifica del dominio. Una libreria è una raccolta di algoritmi riutilizzabili. O uno ti chiederà di dipendere da esso. Dipende da te se vuoi codificare in modo rigido la dipendenza o renderla facilmente sostituibile.
candied_orange

2

Ottiene i dettagli di implementazione delle perdite e interrompe l'astrazione

Ritenere public int millisecondsSince1970()

I tuoi clienti penseranno "oh, questo è un int" e ci saranno chiamate gazillion supponendo che sia un int , facendo matematica intera confrontando le date, ecc ... Quando ti rendi conto che hai bisogno di un lungo , ci saranno molte di codice legacy con problemi. In un mondo Java, aggiungeresti @deprecatedall'API, tutti la ignoreranno e sei bloccato a mantenere il codice obsoleto e difettoso. :-( (suppongo che altre lingue siano simili!)

In questo caso particolare, non sono sicuro di quale possa essere un'opzione migliore e la risposta di @candiedorange è assolutamente corretta, ci sono molte volte in cui hai bisogno di getter, ma questo esempio illustra gli aspetti negativi. Ogni pubblico getter tende a "bloccarti" in una certa implementazione. Usali se hai davvero bisogno di "oltrepassare i confini", ma usa il meno possibile e con cura e lungimiranza.


Questo è vero, ma qual è la soluzione del modello per imporre un tipo / struttura di dati alla variabile di un oggetto, nonché a tutto ciò che usa il getter per quella variabile?
coolpasta,

1
Questo esempio illustra un fenomeno diverso, l'ossessione primitiva. Oggi la maggior parte delle librerie e dei framework hanno classi che rappresentano timepointe timespanrispettivamente. ( epochè un valore particolare di timepoint.) Su queste classi, forniscono getter come getInto getLongo getDouble. Se le classi che manipolano il tempo sono scritte in modo che (1) deleghino l'aritmetica relativa al tempo a questi oggetti e metodi e (2) forniscano l'accesso a questi oggetti del tempo tramite getter, allora il problema è risolto, senza la necessità di liberarsi dei getter .
rwong

1
Riassumendo, i getter fanno parte dell'API, e quindi i getter devono essere progettati con considerazioni sufficienti per tutte le conseguenze, comprese quelle passate, attuali e future. Ma ciò non porta a una conclusione sull'evitare o ridurre al minimo il numero di getter. In effetti, se in futuro è necessario accedere a una particolare informazione per la quale è stato omesso un getter, sarebbe necessario un cambio API lato libreria e un cambio di codice. Pertanto, la decisione se un particolare getter debba essere implementato o evitato dovrebbe essere basata sul dominio e l'intento, non sulla ragione preventiva.
rwong

1
"millisecondiSince1970" ha smesso di funzionare per 32 bit prima della fine di gennaio 1970, quindi dubito che ci sarà molto codice per usarlo :-)
gnasher729

1
@rwong Un po 'corretto, ma si assume che le librerie preferite che rappresentano i punti temporali siano stabili. Che non sono. Ad esempio moment.js
user949300,

1

Penso che la prima chiave sia ricordare che gli assoluti sono sempre sbagliati.

Prendi in considerazione un programma di rubrica, che memorizza semplicemente le informazioni di contatto delle persone. Ma facciamolo bene e suddividiamolo in livelli controller / servizio / repository.

Ci sarà una Personclasse che contiene dati reali: nome, indirizzo, numero di telefono, ecc. AddressE Phonesono anche classi, quindi possiamo usare il polimorfismo in seguito per supportare schemi non statunitensi. Tutte e tre queste classi sono oggetti di trasferimento dati , quindi non saranno altro che getter e setter. E, questo ha un senso: non avranno alcuna logica al di là di ignorare equalse getHashcode; chiedere loro di darti una rappresentazione delle informazioni di una persona completa non ha senso: è per la presentazione HTML, un'app GUI personalizzata, un'app console, un endpoint HTTP, ecc.?

È qui che entrano in gioco il controller, il servizio e il repository. Tutte queste classi saranno logiche e getter-light, grazie all'iniezione di dipendenza.

Il controller otterrà un servizio iniettato, che esporrà metodi come gete save, entrambi i quali prenderà alcuni argomenti ragionevoli (ad esempio, getpotrebbe avere sostituzioni per la ricerca per nome, ricerca per codice postale o semplicemente ottenere tutto; saveprobabilmente prenderà un singolo Persone salvarlo). Quali getter avrebbero il controller? Essere in grado di ottenere il nome della classe concreta potrebbe essere utile per la registrazione, ma quale stato conterrà se non quello che è stato iniettato in esso?

Allo stesso modo, al livello di servizio verrà iniettato un repository, quindi può effettivamente ottenere e persistere Persone potrebbe avere una "logica aziendale" (ad es., Forse avremo bisogno che tutti gli Personoggetti abbiano almeno un indirizzo o telefono numero). Ma, ancora: quale stato ha quell'oggetto oltre a quello che viene iniettato? Ancora una volta, nessuna.

Il livello del repository è dove le cose diventano un po 'più interessanti: stabilirà una connessione a una posizione di archiviazione, ad es. un file, un database SQL o un archivio in memoria. È allettante, qui, aggiungere un getter per ottenere il nome file o la stringa di connessione SQL, ma questo è lo stato che è stato iniettato nella classe. Il repository dovrebbe avere un getter per dire se è collegato correttamente al suo archivio dati? Bene, forse, ma a che cosa servono quelle informazioni al di fuori della classe del repository stesso? La stessa classe di repository dovrebbe essere in grado di tentare di riconnettersi al suo archivio di dati, se necessario, il che suggerisce che la isConnectedproprietà ha già un valore dubbio. Inoltre, una isConnectedproprietà sarà probabilmente sbagliata proprio quando avrebbe più bisogno di essere corretta: il controlloisConnectedprima di tentare di ottenere / archiviare dati non garantisce che il repository sarà comunque connesso quando viene effettuata la chiamata "reale", quindi non rimuove la necessità di una gestione delle eccezioni da qualche parte (ci sono argomenti per dove dovrebbe andare, oltre la portata di questa domanda).

Considera anche unit test (stai scrivendo unit test, giusto?): Il servizio non si aspetta che venga iniettata una classe specifica, ma si aspetta che venga iniettata un'implementazione concreta di un'interfaccia. Ciò consente all'applicazione nel suo insieme di modificare i dati in cui sono archiviati senza dover fare altro che scambiare il repository che viene iniettato nel servizio. Inoltre, consente di testare l'unità di servizio iniettando un repository fittizio. Ciò significa pensare in termini di interfacce piuttosto che di classi. L'interfaccia del repository espone eventuali getter? Cioè, c'è uno stato interno che sarebbe: comune a tutti i repository, utile al di fuori del repository e non iniettato nel repository? Mi farebbe fatica a pensare a me stesso.

TL; DR

In conclusione: a parte le classi il cui unico scopo è portare in giro i dati, nient'altro nello stack ha un posto dove mettere un getter: lo stato viene iniettato o irrilevante al di fuori della classe lavoratrice.


Trovo questa linea di pensiero molto simile al dibattito sull'uso corretto delle proprietà C #. In breve, si prevede che le proprietà (che possono essere solo getter o coppie getter-setter) rispettino un determinato contratto, come "ciò che si imposta è ciò che si ottiene", "il getter dovrebbe essere per lo più privo di effetti collaterali", "getter dovrebbe evitare di gettare errori", ecc. Contrariamente alla tua conclusione, ci sono molti stati oggetto che sono utili per essere esposti come proprietà (o getter). Gli esempi sono troppo numerosi per essere citati qui.
rwong

2
@rwong Sarei interessato a conoscere alcuni esempi, diciamo per un'applicazione "normale" "aziendale" implementata da un team. Supponiamo anche che, per semplicità, non vi siano terze parti coinvolte. Sai, un sistema autonomo, come un calendario, un negozio di animali, qualunque cosa tu voglia.
Robert Bräutigam,

1

Qual è una cosa, senza opinioni, che si romperà se uso i getter in modo ragionevole e per i dati che chiaramente l'integrità dell'oggetto non si rompe se lo mette in evidenza?

Membri mutevoli.

Se hai un insieme di cose in un oggetto che esponi tramite un getter, ti esponi potenzialmente a bug associati all'aggiunta di cose sbagliate nella raccolta.

Se hai un oggetto mutevole che stai esponendo tramite un getter, ti stai potenzialmente aprendo allo stato interno del tuo oggetto che viene modificato in un modo che non ti aspetti.

Un esempio davvero negativo sarebbe un oggetto che sta contando da 1 a 100 esponendo il suo valore corrente come riferimento mutabile. Ciò consente a un oggetto di terze parti di modificare il valore di Corrente in modo tale che il valore non rientri nei limiti previsti.

Questo è principalmente un problema con l'esposizione di oggetti mutabili. Esporre strutture o oggetti immutabili non è affatto un problema, poiché qualsiasi modifica a questi cambierà invece una copia (di solito o da una copia implicita nel caso di una struttura o da una copia esplicita nel caso di un oggetto immutabile).

Usare una metafora che potrebbe rendere più facile vedere la differenza.

Un fiore ha un numero di cose che sono uniche al riguardo. Ha un Colore ha un numero di petali (NumPetals). Se osservo quel fiore, nel mondo reale, posso vedere chiaramente il suo colore. Posso quindi prendere decisioni basate su quel colore. Ad esempio, se il colore è nero, non dare alla ragazza, ma se il colore è rosso, dare alla ragazza. Nel nostro modello a oggetti, il colore del fiore sarebbe esposto come un getter sull'oggetto fiore. La mia osservazione di quel colore è importante per le azioni che eseguirò, ma non ha alcun impatto sull'oggetto fiore. Non dovrei essere in grado di cambiare il colore di quel fiore. Allo stesso modo, non ha senso nascondere la proprietà color. Un fiore generalmente non può impedire alle persone di osservare il suo colore.

Se tingo il fiore, dovrei chiamare il metodo ReactToDye (Color dyeColor) sul fiore, che cambierà il fiore secondo le sue regole interne. Posso quindi interrogare Colornuovamente la proprietà e reagire a qualsiasi modifica dopo che è stato chiamato il metodo ReactToDye. Sarebbe sbagliato per me modificare direttamente Coloril fiore e se potessi allora l'astrazione si è rotta.

A volte (meno frequentemente, ma ancora abbastanza spesso che vale la pena menzionare) i setter sono design OO abbastanza validi. Se ho un oggetto Cliente è abbastanza valido esporre un setter per il loro indirizzo. Sia che si chiama che setAddress(string address)o ChangeMyAddressBecauseIMoved(string newAddress)o semplicemente string Address { get; set; }è una questione di semantica. Lo stato interno di quell'oggetto deve cambiare e il modo appropriato per farlo è quello di impostare lo stato interno di quell'oggetto. Anche se ho bisogno di un registro storico degli indirizzi in cui Customerè vissuto, posso usare il setter per cambiare il mio stato interno in modo appropriato per farlo. In questo caso non ha senso Customerche sia immutabile e non c'è modo migliore per cambiare un indirizzo che fornire un setter per farlo.

Non sono sicuro chi stia suggerendo che Getter e setter siano cattivi o rompano i paradigmi orientati agli oggetti, ma se lo fanno, probabilmente lo stanno facendo in risposta a una specifica caratteristica del linguaggio che usano. Ho la sensazione che questo sia nato dalla cultura Java "tutto è un oggetto" (e forse si è esteso ad altre lingue). Il mondo .NET non ha affatto questa discussione. Getter e setter sono una funzionalità linguistica di prima classe che viene utilizzata non solo dalle persone che scrivono applicazioni nella lingua, ma anche dall'API della lingua stessa.

Dovresti stare attento quando usi i getter. Non vuoi esporre i pezzi di dati sbagliati e non vuoi esporre i dati nel modo sbagliato (ad esempio oggetti mutabili, riferimenti a strutture che possono consentire a un consumatore di modificare lo stato interno). Ma vuoi modellare i tuoi oggetti in modo che i dati siano incapsulati in modo tale che i tuoi oggetti possano essere utilizzati da consumatori esterni e protetti da abusi da parte di quegli stessi consumatori. Spesso ciò richiederà getter.

Riassumendo: Getter e setter sono validi strumenti di progettazione orientati agli oggetti. Non devono essere usati in modo così liberale da esporre ogni dettaglio dell'implementazione, ma non vuoi usarli così con parsimonia che i consumatori di un oggetto non possano usare l'oggetto in modo efficiente.


-1

Qual è una cosa, senza opinioni, che si romperà se uso getter ...

La manutenibilità si interromperà.

Ogni volta (dovrei dire quasi sempre) offri un getter che sostanzialmente dai via più di quanto ti è stato chiesto. Questo significa anche che devi mantenere più di quanto ti è stato chiesto, quindi abbassi la manutenibilità senza una vera ragione.

Andiamo con l' Collectionesempio di @ candied_orange . Contrariamente a quanto scrive Collection , non dovrebbe avere un getter. Le raccolte esistono per motivi molto specifici, in particolare per scorrere su tutti gli articoli. Questa funzionalità non è una sorpresa per nessuno, quindi dovrebbe essere implementata in Collection, invece di spingere questo ovvio caso d'uso sull'utente, usando for-cycle e getter o quant'altro.

Per essere onesti, alcuni confini fanno getter necessità. Questo succede se davvero non sai a cosa serviranno. Ad esempio Exception, al giorno d'oggi è possibile ottenere a livello di codice lo stacktrace dalla classe Java , perché la gente voleva usarlo per tutti i tipi di motivi curiosi che gli scrittori non avevano o non potevano immaginare.


1
Controesempio. Considera una funzione che deve ripetere due Collections contemporaneamente, come in (A1, B1), (A2, B2), .... Ciò richiede un'implementazione di "zip". Come si implementa "zip" se la funzione iterate-over è implementata su uno dei due Collection- come si accede all'elemento corrispondente nell'altro?
rwong

@rwong CollectionDeve implementare zipcon se stesso, a seconda di come è scritta la libreria. In caso contrario, lo implementate e inviate una richiesta pull al creatore della libreria. Nota che ho concordato che alcuni confini hanno bisogno di getter a volte, il mio vero problema è che i getter sono ovunque al giorno d'oggi.
Robert Bräutigam,

In tal caso, ciò implica che la libreria deve disporre di getter Collectionsull'oggetto, almeno per la propria implementazione di zip. (Cioè, il getter deve avere almeno la visibilità del pacchetto.) E ora
dipendi

Non ti capisco del tutto. L' Collectionovviamente può accedere il proprio stato interno, o per essere più precisi qualsiasi Collectionistanza può accedere qualsiasi altro stato interno. È dello stesso tipo, non è necessario alcun getter.
Robert Bräutigam,

Anche io ti sento, ma poi, qual è esattamente la soluzione migliore per i ginecologi? So che è fuori dalla portata della domanda.
coolpasta,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.