Domanda

stato a guardare alcuni video Greg Young ultimamente e sto cercando di capire il motivo per cui v'è un atteggiamento negativo verso Setter su oggetti di dominio. Ho pensato che gli oggetti di dominio dovevano essere "pesante" con la logica in DDD. Ci sono dei buoni esempi in linea del cattivo esempio e poi modo di correggere questo? Eventuali esempi o spiegazioni sono buone. Questo si applica solo agli eventi memorizzati in un modo o CQRS questo si applica a tutti DDD?

È stato utile?

Soluzione

sto contribuendo questa risposta per completare Roger Alsing risposta su invarianti con altri motivi.

informazioni semantiche

Roger chiaramente spiegato che setter di proprietà non trasportano informazioni semantiche. Permettendo un setter per una proprietà come Post.PublishDate può aggiungere confusione in quanto non possiamo sapere con certezza se il post è stato pubblicato o solo se la data di pubblicazione è stata impostata. Non possiamo sapere per certo che questo è tutto ciò che è necessario per pubblicare un articolo. L'oggetto "interfaccia" non mostra la sua "intenzione" chiaramente.

Credo, però, che le proprietà come "Enabled" fare trasportare abbastanza semantica sia per "impostazione" "ottenere" e. E 'qualcosa che dovrebbe essere immediatamente efficace e non riesco a vedere la necessità di setactive () o Attivare () / disattivare () i metodi per la sola mancanza della semantica sul setter di proprietà della ragione.

invarianti

Roger ha anche parlato della possibilità di rompere invarianti attraverso setter di proprietà. Questo è assolutamente giusto, e si dovrebbe far proprietà che lavorano in tandem per fornire un "valore invariante combinato" (come gli angoli di triangolo per usare l'esempio di Roger) come proprietà di sola lettura e creare un metodo per impostare tutti insieme, che può validare tutte le combinazioni in un unico passaggio.

di inizializzazione ordine Proprietà / impostazione dipendenze

Questo è simile al problema con invarianti in quanto causa problemi con la proprietà che devono essere convalidati / modificati insieme. Immaginate il seguente codice:

    public class Period
    {
        DateTime start;
        public DateTime Start
        {
            get { return start; }
            set
            {
                if (value > end  && end != default(DateTime))
                    throw new Exception("Start can't be later than end!");
                start = value;
            }
        }

        DateTime end;
        public DateTime End
        {
            get { return end; }
            set
            {
                if (value < start && start != default(DateTime))
                    throw new Exception("End can't be earlier than start!");
                end = value;
            }
        }
    }

Questo è un esempio ingenua di validazione "setter" che causa dipendenze di ordine di accesso. Il codice seguente illustra questo problema:

        public void CanChangeStartAndEndInAnyOrder()
        {
            Period period = new Period(DateTime.Now, DateTime.Now);
            period.Start = DateTime.Now.AddDays(1); //--> will throw exception here
            period.End = DateTime.Now.AddDays(2);
            // the following may throw an exception depending on the order the C# compiler 
            // assigns the properties. 
            period = new Period()
            {
                Start = DateTime.Now.AddDays(1),
                End = DateTime.Now.AddDays(2),
            };
            // The order is not guaranteed by C#, so either way may throw an exception
            period = new Period()
            {
                End = DateTime.Now.AddDays(2),
                Start = DateTime.Now.AddDays(1),
            };
        }

Dal momento che non siamo in grado di cambiare la data di inizio oltre la data di fine su un oggetto periodo (a meno che non si tratta di un "vuoto" periodo, con entrambe le date stabilite per default (DateTime) - sì, non è un grande disegno, ma si ottiene quello che voglio dire ...) cercando di impostare la data di inizio primo sarà un'eccezione.

Si diventa più grave quando usiamo inizializzatori di oggetto. Dal momento che C # non garantiscono alcun ordine di assegnazione, non possiamo fare alcuna ipotesi sicure e il codice può o non può generare un'eccezione a seconda delle scelte del compilatore. BAD!

Questa è in definitiva un problema con il disegno delle classi. Dal momento che la proprietà non può "sapere" che si sta aggiornando entrambi i valori, non può "spegnere" la convalida finché entrambi i valori sono in realtà cambiato. Si dovrebbe o fare entrambe le proprietà di sola lettura e di fornire un metodo per impostare entrambi allo stesso tempo (perdendo la caratteristica di inizializzatori oggetto) o rimuovere il codice di convalida dalle proprietà del tutto (introducendo forse un altro proprietà di sola lettura, come IsValid, o la convalida in caso di necessità).

ORM "idratazione" *

L'idratazione, in una visione semplicistica, mezzi ottenere i dati persistenti indietro in oggetti. Per me questo è davvero il problema più grande con l'aggiunta di logica che sta dietro setter di proprietà.

Molti / la maggior parte ORM mappa il valore persistente in una proprietà. Se si dispone di logica di convalida o la logica che cambia lo stato dell'oggetto (gli altri membri) all'interno setter di proprietà si finirà per cercare di convalidare contro un oggetto "incompleta" (uno ancora in fase caricata). Questo è molto simile al problema oggetto di inizializzazione dal momento che non è possibile controllare l'ordine i campi sono "idratati".

La maggior parte ORM consentono di mappare la persistenza di campi privati, invece di proprietà, e questo permetterà agli oggetti di essere idratata, ma se le vostre bugie logica di convalida per lo più setter di proprietà all'interno potrebbe essere necessario duplicarlo altrove per verificare che un carico oggetto è valido o meno.

Poiché molti strumenti ORM supportano lazy loading (un aspetto fondamentale di un ORM!) Attraverso l'uso di strutture virtuali (o metodi) mappatura ai campi comporta l'impossibilità per l'ORM di oggetti di carico pigri mappati in campi.

Conclusione

Così, alla fine, al codice di evitare duplicazioni, permettono ORM di eseguire come meglio potevano, impediscono eccezioni sorprendenti a seconda dell'ordine dei campi sono impostati, è saggio logica allontanamento dal setter di proprietà.

Sono ancora capire dove questa logica 'convalida' dovrebbe essere. Dove possiamo convalidare l'invarianti aspetti di un oggetto? Dove mettiamo le convalide di livello superiore? Usiamo ganci sulle ORM per eseguire la convalida (OnSave, Su eliminazione, ...)? Ecc ecc ecc Ma questo non è lo scopo di questa risposta.

Altri suggerimenti

Setter doesnt porta alcuna informazione semantica.

per es.

blogpost.PublishDate = DateTime.Now;

Questo significa che il post è stato pubblicato? O semplicemente che la data di pubblicazione sono state impostate?

Si consideri:

blogpost.Publish();

Ciò dimostra chiaramente l'intenzione che il blogpost dovrebbe essere pubblicato.

Inoltre, setter possono rompere le invarianti degli oggetti. Per esempio, se abbiamo un soggetto "Triangolo", l'invariante dovrebbe essere che la somma di tutti gli angoli dovrebbe essere di 180 gradi.

Assert.AreEqual (t.A + t.B + t.C ,180);

Ora, se abbiamo setter, si può facilmente rompere le invarianti:

t.A = 0;
t.B = 0;
t.C = 0;

Quindi abbiamo un triangolo in cui la somma di tutti gli angoli sono 0 ... È davvero un triangolo? Direi di no.

La sostituzione setter con metodi noi potrebbe costringere a mantenere invarianti:

t.SetAngles(0,0,0); //should fail 

Questa chiamata dovrebbe generare un'eccezione dicendo che questo provoca uno stato non valido per il vostro organismo.

Così si ottiene la semantica e invarianti con metodi al posto di setter.

La ragione di questo è che l'entità in sé dovrebbe essere responsabile per cambiare il suo stato. Non c'è davvero alcun motivo per cui si dovrebbe essere necessario impostare un qualsiasi punto proprietà al di fuori dell'entità stessa.

Un semplice esempio sarebbe un ente che ha un nome. Se si dispone di un setter pubblico si sarebbe in grado di cambiare il nome di un'entità da qualsiasi parte voi applicazione. Se si rimuove invece che setter e mettere un metodo come ChangeName(string name) al soggetto che sarà l'unico modo per cambiare il nome. In questo modo è possibile aggiungere qualsiasi tipo di logica che sarà sempre eseguito quando si modifica il nome perché non v'è solo un modo per cambiarlo. Questo è anche molto più chiaro quindi solo l'impostazione del nome a qualcosa.

In pratica questo significa che si espone il comportamento sulle proprie entità pubblicamente, mentre si mantiene lo stato in privato.

La domanda originale è Tagged .net, quindi mi invio un approccio pragmatico per il contesto in cui si desidera collegare i soggetti direttamente ad una vista.

So che questa è cattiva pratica, e che probabilmente si dovrebbe avere un modello di vista (come in MVVM) o simili, ma per alcune piccole applicazioni solo senso di non over-patternize IMHO.

Uso delle proprietà è la via out-of-box i dati opere vincolanti in .NET. Forse i stipula di contesto che i dati di legame dovrebbe funzionare in entrambi i modi, e quindi di esecuzione INotifyPropertyChanged e sollevano PropertyChanged come parte della logica setter ha un senso.

L'entità potrebbe ad esempio aggiungere un elemento a una raccolta delle regole rotto o simili (So CSLA aveva quel concetto di qualche anno fa e forse ancora lo fa) quando il client imposta un valore non valido, e che la raccolta potrebbe essere mostrato nell'interfaccia utente. L'unità di lavoro sarebbe poi rifiutare di persistere oggetti non validi, se dovesse venire a tanto.

Sto cercando di giustificare il disaccoppiamento, immutabilità, e così via, in larga misura. Sto solo dicendo che, in alcuni contesti un'architettura più semplice è richiesto.

Un setter imposta semplicemente un valore. Non si suppone che sia "heavy" with logic.

metodi su oggetti che hanno buoni nomi descrittivi dovrebbero essere quelle "heavy" with logic e hanno un analogo del dominio stesso.

consiglio vivamente la lettura del DDD libro di Eric Evans e Object-Oriented Software Construction da Bertrand Meyer . Hanno tutti i campioni che si sarebbe mai bisogno.

I può essere fuori qui, ma credo che i clienti dovrebbero essere utilizzati per impostare, a differenza di metodi setter. Ho un paio di ragioni per questo.

a) Ha senso in .Net. Ogni sviluppatore conosce proprietà. Ecco come si imposta le cose su un oggetto. Perché si discostano da quello per gli oggetti di dominio?

b) Setter possono avere il codice. Prima di 3,5 io credo, l'impostazione di un oggetto costituito da una variabile interna e una firma di proprietà

private object _someProperty = null;
public object SomeProperty{
    get { return _someProperty; }
    set { _someProperty = value; }
}

E 'molto facile e imo elegante per mettere convalida nel setter. In IL, getter e setter vengono convertiti in metodi comunque. Perché il codice duplicato?

Nel esempio del metodo di pubblicazione () ha postato sopra, sono completamente d'accordo. Ci sono momenti in cui non vogliamo altri sviluppatori impostazione di una proprietà. Questo dovrebbe essere gestita con un metodo. Tuttavia fa senso avere un metodo setter per ogni proprietà quando Net fornisce tutte le funzionalità di cui abbiamo bisogno della dichiarazione di proprietà già?

Se si dispone di un oggetto di una persona, perché creare metodi per ogni proprietà su di essa, se non c'è nessuna ragione?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top