2010-03-16 19 views
5

Mi piacerebbe avere la tua opinione su quanto lontano andare con setter senza effetti collaterali.Approccio ai setter senza effetti collaterali

consideri il seguente esempio:

Activity activity; 
activity.Start = "2010-01-01"; 
activity.Duration = "10 days"; // sets Finish property to "2010-01-10" 

noti che i valori per data e durata sono mostrati solo a titolo indicativo.

Quindi, utilizzando setter per qualsiasi proprietà Start, Finish e Duration saranno pertanto modificare altre proprietà e quindi non può essere considerato indenne effetto collaterale-. Lo stesso vale per le istanze della classe Rectangle, dove setter per X sta modificando i valori di Top e Bottom e così via.

La domanda è dove si dovrebbe tracciare una linea tra l'utilizzo di setter, che hanno effetti collaterali di modifica dei valori di proprietà correlate logicamente e l'utilizzo di metodi, che tuttavia non potrebbero essere molto più descrittivi. Ad esempio, la definizione di un metodo chiamato SetDurationTo(Duration duration) non riflette neanche l'inizio o la fine della modifica.

risposta

8

Penso che tu abbia frainteso il termine "effetto collaterale" come si applica al design del programma. L'impostazione di una proprietà è un effetto collaterale, non importa quanto o quanto piccolo stato interno viene modificato, purché cambi lo stato dello stato. Un "setter senza effetti collaterali" non sarebbe molto utile.

Gli effetti collaterali sono qualcosa che si desidera evitare sulla proprietà getters. Leggere il valore di una proprietà è qualcosa che il chiamante non si aspetta di cambiare alcuno stato (cioè causa effetti collaterali), quindi se lo fa, di solito è sbagliato o almeno discutibile (ci sono eccezioni, come il caricamento lazy). Ma i getter e gli inseguitori allo stesso modo sono solo involucri per i metodi comunque. La proprietà Duration, per quanto riguarda il CLR, è solo zucchero sintattico per un metodo set_Duration.

Questo è esattamente ciò che le astrazioni come le classi sono intese per: fornire operazioni a grana grossa mantenendo uno stato interno coerente. Se si tenta deliberatamente di evitare di avere più effetti collaterali in una singola assegnazione di proprietà, le classi finiscono per non essere molto più di stupidi contenitori di dati.

Quindi, rispondendo direttamente alla domanda: dove disegno la linea? Da nessuna parte, purché il metodo/proprietà faccia effettivamente quello che il suo nome implica. Se l'impostazione di Duration ha modificato anche ActivityName, potrebbe essere un problema. Se cambia la proprietà Finish, dovrebbe essere ovvio; è dovrebbe impossibile cambiare il Duration e avere sia il Start e Finish rimanere lo stesso. La premessa di base di OOP è che gli oggetti sono abbastanza intelligenti da gestire queste operazioni da soli.

Se questo ti dà fastidio a livello concettuale, allora non hai affatto proprietà di mutatore - usa una struttura di dati immutabile con proprietà di sola lettura dove tutti gli argomenti necessari sono forniti nel costruttore. Quindi hanno due sovraccarichi, uno che prende uno Start/Duration e un altro che prende uno Start/Finish. Oppure rendi scrivibile solo una delle proprietà, diciamo Finish per renderla coerente con Start, quindi rendi la lettura sola Duration. Utilizzare la combinazione appropriata di proprietà mutabili e immutabili per garantire che vi sia un solo modo per modificare un determinato stato.

Altrimenti, non preoccuparti così tanto di questo. Proprietà (e metodi) non dovrebbero avere non voluto o non documentato effetti collaterali, ma questa è l'unica linea guida che userei.

+0

Grazie, questo è quello che stavo cercando e non mi viene mai in mente l'effetto collaterale -sempre libero "in realtà non è possibile in quanto cambierà lo stato. E come hai sottolineato, il CLR tradurrà comunque il metodo. – Martin

+0

Sì, l'effetto collaterale di un setter è impostare * la variabile membro a cui si riferisce *.Ma un ulteriore effetto collaterale è quello di modificare * altre * variabili membro. –

+0

Stavo pensando alla stessa cosa e direi che l'impostazione della variabile membro non è un effetto collaterale, ma l'effetto desiderato del setter. Il cambio di altre variabili membro è un effetto collaterale. – Martin

0

Ho sempre lavorato con la regola generale di non consentire i setter public su proprietà che non sono a effetto collaterale poiché i chiamanti dei vostri setter pubblici non possono essere certi di ciò che potrebbe accadere, ma naturalmente, le persone che modificano il l'assemblaggio stesso dovrebbe avere una buona idea in quanto possono vedere il codice.

Naturalmente, ci sono sempre dei casi in cui è necessario infrangere la regola per motivi di leggibilità, per rendere il proprio modello di oggetto logico o semplicemente per far funzionare le cose correttamente. Come hai detto tu, è una questione di preferenza in generale.

1

Personalmente, penso che abbia senso avere un effetto collaterale per mantenere uno stato coerente. Come hai detto tu, ha senso cambiare i valori relativi alla logica. In un certo senso, è previsto l'effetto collaterale. Ma l'importante è chiarire questo punto. Cioè, dovrebbe essere evidente che il compito che il metodo sta eseguendo ha una sorta di effetto collaterale. Quindi, invece di SetDurationTo potresti chiamare la tua funzione ChangeDurationTo, che implica qualcos'altro sta succedendo. Puoi anche farlo in un altro modo avendo una funzione/metodo che regola la durata AdjustDurationTo e passa in un valore delta. Sarebbe utile se si documentasse la funzione come avente un effetto collaterale.

Penso che un altro modo per vederlo è vedere se è previsto un effetto collaterale. Nell'esempio di un rettangolo, mi aspetto che cambi i valori di top o bottom per mantenere uno stato coerente internamente. Non so se questo è soggettivo; sembra avere senso per me. Come sempre, penso che la documentazione vince. Se c'è un effetto collaterale, documentalo molto bene. Preferibilmente dal nome del metodo e attraverso la documentazione di supporto.

-2

Penso che sia principalmente una questione di buonsenso.

In questo particolare esempio, il mio problema non è tanto che hai proprietà che regolano le proprietà "correlate", è che hai delle proprietà che assumono valori di stringa che poi stai analizzando interiormente in DateTime (o qualsiasi altra cosa) valori.

Avrei preferito vedere qualcosa del genere:

Activity activity; 
activity.Start = DateTime.Parse("2010-01-01"); 
activity.Duration = Duration.Parse("10 days"); 

Cioè, è esplicitamente presente che si sta facendo il parsing di stringhe.Consentire al programmatore di specificare oggetti con caratteri forti anche quando questo è appropriato.

+0

Come ho menzionato "Si noti che i valori per data e durata sono mostrati solo a scopo indicativo." Quindi il tuo commento in realtà non risponde alla domanda, ma grazie comunque ... Sapevo che qualcuno ti indicherà questo ;-) – Martin

1

Un'opzione consiste nel rendere la classe immutabile e avere metodi per creare e restituire nuove istanze della classe che hanno modificato tutti i valori appropriati. Quindi non ci sono effetti collaterali o setter. Pensa a qualcosa come DateTime dove puoi chiamare cose come AddDays e AddHours che restituirà una nuova istanza DateTime con la modifica applicata.

Problemi correlati