Logica in setter contro getter?

7

Okey dokey, il prossimo crappy-code-at-work-rant-diventa-in-a-real-question (spero):

Quindi sto eseguendo il debug di alcuni codici e abbiamo qualcosa di simile:

objFoo.TabIndex = 5   'VB.NET property with setter implicitly called

e TabIndex è una proprietà il cui setter è l'inizio di un albero di chiamata insano , con subroutine e XML e casi speciali a seconda del contesto (come: modalità) e così via.

(Gack. Puoi vedere cosa mi sta facendo lavorare qui. Ho appena aggiunto a Foo "obj". :()

Ok, garantito, dobbiamo gestire lo stato ed eseguire calcoli e quant'altro. Il nostro stato è nel database (ma potrebbe essere costoso da estrarre) o non è ancora pronto per essere impegnato nel database (ad esempio, è solo parzialmente cotto), quindi non possiamo limitarci a esaurire il d / b ogni volta che abbiamo bisogno di informazioni, ma ...

È meglio gestire questo stato nei setter o è meglio farlo nei getter?

Penserei che farlo in getters (cioè, pigramente) significherebbe che calcoleremmo (e, presumibilmente, cache) solo ciò di cui abbiamo bisogno, mentre lo facciamo in setter significa che stiamo essenzialmente superando il calcolo, anticipando ciò che i getters possono essere chiamati in futuro e precomputeranno i loro risultati. (E poi, ci sono i membri dei dati che non hanno getter, sono solo variabili globali in una classe di 6.000 righe.)

Non dovremmo semplicemente, come regola generale, impostare il membro dei dati indicato, magari impostare dei flag che indicano che altri dati non sono più validi (invalidazione della cache) e aspettare che i vari getter vengano chiamati a ricalcolare?

Posso immaginare che un argomento per fare questo nei setter è che il nostro oggetto è così complesso che abbiamo davvero bisogno di configurare correttamente il suo stato, usando più membri di dati complessi. Ma non è una bandiera rossa in sé, indicando che un approccio migliore sarebbe quello di rompere l'oggetto in oggetti più piccoli, non scrivere un setter complesso?

(Ora, so che ci sono eccezioni di ogni genere che la gente senza dubbio sottolinea, ma sto cercando una regola empirica generale e la logica alla base, o l'affermazione che non esiste una tale regola generale possibile, accompagnato da un argomento convincente.)

Grazie.

John.

    
posta JohnL4 15.07.2011 - 18:00
fonte

4 risposte

14

In generale, se la logica richiede un tempo non banale da eseguire, dovrebbe essere eseguita in un metodo, non in un getter o in un setter.

Per convenzione, i getter e i setter dovrebbero tornare quasi immediatamente, mentre ci si aspetta che i metodi impieghino ... un po 'di più.

    
risposta data 15.07.2011 - 18:10
fonte
4

Non esporre lo stato

preferire

obj.SetTabOrdering(3);

per due motivi:

  1. nasconde l'uso della variabile di stato TabIndex, nel caso in cui cambi in futuro

  2. implica (perché è un metodo) che può avvenire un'ulteriore elaborazione

In generale:

  • setter e getter per le proprietà non devono eseguire calcoli estesi
  • setter e getter per le proprietà dovrebbero non essere esposti nell'interfaccia pubblica se evitabili

Questi ultimi aiutano a garantire che le modifiche dello stato interno non causino modifiche all'interfaccia esterna.

Incapsulamento + Interfacce: nascondi il tuo stato. Esponi i tuoi metodi.

    
risposta data 15.07.2011 - 19:05
fonte
1

Per quanto riguarda la memorizzazione nella cache delle proprietà, la considererei caso per caso. Nella tua proprietà TabIndex di esempio, si riferisce alla scheda visualizzata o qualcos'altro? Se è la scheda visualizzata, è ovvio che è necessario aggiornarla immediatamente, piuttosto che aspettare che qualcosa interroghi il suo stato e lasciare eventualmente la GUI obsoleta. Inoltre, l'uso della memorizzazione nella cache potrebbe velocizzare i tempi impostati della tua proprietà, ma ora potresti eseguire tutti gli aggiornamenti della cache nelle tue chiamate di get , che sono ancora meno preferite.

Ho chiesto una domanda simile qualche tempo fa, e quello che ho concluso è questo: i getter e gli incastonatori dovrebbero non esegue autonomamente operazioni estese, ma può generare eventi che eseguono una buona dose di logica. Ad esempio, aggiornando TabIndex si otterrebbe TabIndexChanged , in cui verrebbe eseguita tutta la logica. Ciò consente di dare un po 'di più al flusso del programma e consente di separare le proprietà dalla logica in un modo semplice e intuitivo, che è molto importante.

Infine, mi piace l'idea che i metodi siano i verbi, le proprietà non siano . Quindi non impostare una proprietà Connect su true per avviare una connessione, chiama invece OpenConnection .

    
risposta data 15.07.2011 - 19:25
fonte
0

Dipende da cosa influenza lo stato e da cosa influenza lo stato, a mio avviso.

Supponendo che lo stato dell'oggetto si riferisca a più del singolo membro, se modificando il valore di quel valore membro lo stato dell'oggetto è interessato, potresti voler gestire lo stato nel setter. Dato che potresti non voler lasciare l'utente in giro in questo caso, potresti semplicemente attivare un evento per gestire la modifica dello stato nel setter, piuttosto che attendere che tutta l'elaborazione correlata continui.

Se il valore del membro, quando viene restituito, dipende dallo stato, allora dovrai controllare lo stato nel getter.

Quindi la connessione tra una qualsiasi variabile membro e lo stato generale dell'oggetto dovrebbe rendere abbastanza chiaro quando, se del caso, è necessario interagire tra il valore della variabile che cambia e viene recuperato e lo stato. Se non è quindi decidere se mettere quella logica nel getter, il setter o altrove è nulla in confronto ai tuoi più ampi problemi di architettura ...

    
risposta data 15.07.2011 - 18:29
fonte

Leggi altre domande sui tag