L'entità del dominio sta violando il principio di responsabilità singola?


13

La singola responsabilità (motivo per cambiare) di un'entità dovrebbe essere quella di identificarsi in modo univoco, in altre parole, la sua responsabilità deve essere individuabile.

Il libro DDD di Eric Evan, pag. 93:

la responsabilità più fondamentale delle Entità è quella di stabilire la continuità in modo che il comportamento possa essere chiaro e prevedibile. Lo fanno meglio se vengono tenuti di riserva. Piuttosto che concentrarsi sugli attributi o persino sul comportamento, eliminare la definizione dell'oggetto Entità fino alle caratteristiche più intrinseche, in particolare quelle che lo identificano o sono comunemente utilizzate per trovarlo o abbinarlo. Aggiungi solo il comportamento essenziale al concetto e agli attributi richiesti da quel comportamento.

Oltre a ciò, cerca di rimuovere il comportamento e gli attributi in altri oggetti associati all'entità principale. Al di là dei problemi di identità, le entità tendono ad adempiere alle loro responsabilità coordinando le operazioni degli oggetti di loro proprietà.

1.

... eliminare la definizione dell'oggetto ENTITY fino alle caratteristiche più intrinseche, in particolare quelle che lo identificano o sono comunemente utilizzate per trovarlo o abbinarlo. Aggiungi solo il comportamento essenziale al concetto ...

Una volta che a un'entità viene assegnato un ID univoco , viene stabilita la sua identità e quindi presumo che tale entità non abbia bisogno di alcun comportamento per mantenere la sua identità o per aiutarla a identificarsi . Quindi, non capisco che tipo di comportamento è autore riferendosi a (oltre finde match operazioni ) con " un comportamento che è essenziale per il concetto di "?

2.

... eliminare la definizione dell'oggetto ENTITY fino alle caratteristiche più intrinseche, in particolare quelle che lo identificano o sono comunemente utilizzate per trovarlo o abbinarlo. ... Oltre a ciò, cerca di rimuovere il comportamento e gli attributi in altri oggetti associati al core ENTITY.

Quindi qualsiasi comportamento che non aiuta a identificare l'entità, ma lo caratterizzeremmo comunque come una caratteristica intrinseca di quell'entità (ad esempio l'abbaiare è intrinseco ai cani, il volo è intrinseco agli aeroplani, la deposizione delle uova è intrinseca agli uccelli ... .), dovrebbe essere inserito in altri oggetti associati a quell'entità (esempio: dovremmo mettere il comportamento di abbaiare in un oggetto associato a un'entità cane)?

3.

Oltre a ciò, cerca di rimuovere il comportamento e gli attributi in altri oggetti associati al core ENTITY.

a) MyEntitydelegare le responsabilità A_respe B_respagli oggetti ae b, rispettivamente.

Anche se la maggior parte A_respe il B_resplavoro è svolto da ae bistanze, i clienti sono ancora serviti A_respe B_respattraverso MyEntity, il che significa che dal punto di vista del cliente appartengono le due responsabilità MyEntity. Quindi, ciò non significa MyEntityanche avere A_respe B_respresponsabilità e come tale sta violando SRP ?

b) Anche se lo ipotizziamo A_respe B_respnon ci apparteniamo MyEntity, MyEntityha ancora la responsabilità AB_respdi coordinare le operazioni degli oggetti ae b. Quindi non MyEntityviola SRP poiché almeno ha due responsabilità : identificarsi in modo univoco e anche AB_resp?

class MyEntity
{
    private A a = ...
    private B b = ...


    public A GetA()
    { ... }

    public B GetB()
    { ... }

    /* coordinates operations of objects a and b */
    public int AworkB()
    { ... }
}

/* A encapsulates a single responsibility resp_A*/
/* A is value object */
class A
{ ... }

/* B encapsulates a single responsibility resp_B*/
/* B is value object */
class B
{ ... }

AGGIORNARE:

1.

Il comportamento in questo contesto si riferisce al comportamento semantico. Ad esempio, una proprietà su una classe (ovvero l'attributo su un oggetto dominio) utilizzata per identificarla in modo univoco ha un comportamento. Mentre questo non è rappresentato direttamente nel codice. Il comportamento previsto è che non ci saranno valori duplicati per quella proprietà.

Quindi nel codice non avremmo quasi mai bisogno di implementare effettivamente un comportamento (cioè un'operazione) che mantenga in qualche modo l'identità dell'entità, poiché come hai spiegato un simile comportamento esiste solo come concetto in un modello di dominio (sotto forma di un attributo ID di un'entità), ma quando traduciamo questo attributo ID in codice, parte della sua semantica viene persa (ovvero la parte che assicura implicitamente che il valore ID sia univoco viene persa)?

2.

Inoltre, una proprietà come Age non ha alcun contesto al di fuori di un'entità persona e, come tale, non ha senso spostarsi in un oggetto diverso ... Tuttavia, tali informazioni potrebbero essere facilmente archiviate in una posizione separata rispetto all'identificatore univoco, quindi il riferimento confuso al comportamento. L'età potrebbe essere un valore caricato pigro.

a) Se la Ageproprietà è lazy load, allora potremmo chiamarla un comportamento, anche se semanticamente Ageè solo un attributo?

3.

Si potrebbero facilmente avere operazioni specifiche per l'indirizzo come la verifica che si tratti di un indirizzo valido. Potresti non saperlo in fase di progettazione, ma l'intero concetto è quello di scomporre gli oggetti nelle loro parti più piccole

Mentre sono d'accordo che perderemmo contesto spostandoci Agein un oggetto diverso, il contesto non andrebbe perso se spostassimo la DateOfBirthproprietà in un oggetto diverso, ma di solito non lo spostiamo.

Qual è la ragione principale per cui dovremmo spostarci Addressin un altro oggetto, ma no DateOfBirth? Perché DateOfBirthè più intrinseco Personall'entità o perché ci sono meno possibilità che da qualche parte in futuro potremmo aver bisogno di definire operazioni specifiche DateOfBirth?

4. Devo dire che ancora non so se MyEntityha anche A_respe B_respresponsabilità e perché MyEntityanche il fatto di AB_respnon essere considerato una violazione di SRP

EULERFX

1)

I comportamenti a cui fa riferimento l'autore sono comportamenti associati all'entità. Questi sono i comportamenti che modificano lo stato dell'entità

a) Se ti capisco correttamente, stai dicendo che l' entità dovrebbe contenere solo quei comportamenti che modificano i suoi attributi (cioè il suo stato )?

b) E i comportamenti che non modificano necessariamente lo stato dell'entità , ma sono comunque considerati come una caratteristica intrinseca di quell'entità (esempio: l' abbaiare sarebbe una caratteristica intrinseca di Dogun'entità, anche se non modificasse Stato del cane )? Dovremmo includere questi comportamenti in un'entità o dovrebbero essere spostati su altri oggetti?

2)

Per quanto riguarda lo spostamento del comportamento su altri oggetti, l'autore si riferisce specificamente agli oggetti valore.

Sebbene la mia citazione non lo includa, ma l'autore menziona nello stesso paragrafo che in alcuni casi i comportamenti (e gli attributi ) verranno anche spostati in altre entità (anche se comprendo i vantaggi dello spostamento dei comportamenti nei VO)

3) Assumendo MyEntity(vedere la domanda 3. nel mio post originale) non viola SRP, diremmo che una responsabilità di MyEntityè tra l'altro anche composti da:

un. A_resp + B_resp + AB_resp ( AB_respcoordina oggetti ae b)

o

b. AB_resp + delegare A_respe B_respagli oggetti ( ae b) associati a MyEntity?

4) Il libro DDD di Eric Evan, pag. 94:

CustomerID è l'unico identificativo di ENTITY cliente (figura 5.5), ma il numero di telefono e l'indirizzo vengono spesso utilizzati per trovare o abbinare un cliente. Il nome non definisce l'identità di una persona, ma viene spesso utilizzato come parte del mezzo per determinarla.

In questo esempio, gli attributi di telefono e indirizzo sono stati spostati in Cliente, ma su un progetto reale, tale scelta dipenderà dal modo in cui i clienti del dominio vengono generalmente abbinati o distinti. Ad esempio, se un cliente ha molti numeri di telefono di contatto per scopi diversi, il numero di telefono non è associato all'identità e deve rimanere con il contatto di vendita.

un)

CustomerID è l'unico identificativo di ENTITY cliente (figura 5.5), ma il numero di telefono e l'indirizzo vengono spesso utilizzati per trovare o abbinare un cliente. Il nome non definisce l'identità di una persona, ma viene spesso utilizzato come parte del mezzo per determinarla.

La citazione afferma che solo gli attributi associati all'identità dovrebbero rimanere in un'entità . Presumo autore significa che l' entità dovrebbe contenere solo quegli attributi che sono spesso usati per trovare o abbinare questa entità , mentre TUTTI gli altri attributi dovrebbero essere spostati?

b) Ma come / dove dovrebbero essere spostati altri attributi ? Ad esempio (ipotesi qui è che l' attributo address non sia usato per trovare o abbinare Customer e quindi vogliamo spostare l' attributo address fuori da Customer):

se invece di avere Customer.Address(di tipo string) creiamo una proprietà Customer.Addressdi tipo Address, spostiamo l' attributo address in un oggetto VO associato (che è di tipo Address) o diremmo che Customercontiene ancora l' attributo address ?

c)

In questo esempio, gli attributi di telefono e indirizzo sono stati spostati in Cliente, ma su un progetto reale, tale scelta dipenderà dal modo in cui i clienti del dominio vengono generalmente abbinati o distinti. Ad esempio, se un cliente ha molti numeri di telefono di contatto per scopi diversi, il numero di telefono non è associato all'identità e deve rimanere con il contatto di vendita.

L'autore non ha torto qui, dal momento che se assumiamo ciascuno dei tanti numeri di telefono di contatto che Customerappartengono solo a quel particolare Customer, allora direi che questi numeri di telefono sono associati all'identità tanto quanto quando Customeraveva un solo numero di telefono ?

5)

La ragione per cui l'autore suggerisce di eliminare l'entità è che quando si crea inizialmente un'entità cliente, c'è la tendenza a popolarla con qualsiasi attributo che si possa pensare di essere associato a un cliente. Questo è un approccio incentrato sui dati che trascura i comportamenti che alla fine portano a un modello di dominio anemico.

Fuori tema, ma ho pensato anemici modello del dominio risultati di muoversi comportamento di un soggetto , mentre il vostro esempio è popolando un soggetto con un sacco di attributi , il che porterebbe a Customerdover troppo comportamento (in quanto ci sarebbe probabilmente includiamo anche Customeri comportamenti che modificare questi attributi aggiuntivi ) e quindi in violazione di SRP?

Grazie


2
Consiglio vivamente la serie di video in codice pulito di robert martins, cleancoders.com. Egli approfondisce dettagliatamente come i diversi principi possono causare problemi o bilanciarsi. altrimenti penso che parte della formula del tuo esempio sarebbe guardare il lasso di tempo in cui l'oggetto Person è interessato. se è per un breve periodo come un acquisto, l'indirizzo di fatturazione utilizzato per l'acquisto sarebbe parte di esso e immutabile. se si tratta di un account Library, l'indirizzo dovrebbe essere in grado di cambiare.
cartalot,

2
Penso che questa domanda potrebbe violare la SRP ...;)
IntelliData

Risposte:


6

Il comportamento in questo contesto si riferisce al comportamento semantico. Ad esempio, una proprietà su una classe (ovvero l'attributo su un oggetto dominio) utilizzata per identificarla in modo univoco ha un comportamento. Mentre questo non è rappresentato direttamente nel codice. Il comportamento previsto è che non ci saranno valori duplicati per quella proprietà. Qualcosa come un indirizzo che può avere una propria identità, ma non esiste al di fuori del contesto di un'entità persona, dovrebbe comunque essere spostato nel suo oggetto. Promuovendo così l'entità in una radice aggregata.

Inoltre, una proprietà come Age non ha alcun contesto al di fuori di un'entità persona e, come tale, non ha senso muoversi in un oggetto diverso. Il contesto andrebbe perso e quindi è possibile determinare con sicurezza che si tratta di un valore essenziale per l'entità persona. Altrimenti non è possibile individuare il valore. Tuttavia, tali informazioni potrebbero essere facilmente archiviate in una posizione separata rispetto all'identificatore univoco, da cui il riferimento confuso al comportamento . L'età potrebbe essere un valore caricato pigro.

Quindi per rispondere alla tua domanda. No, non viola il principio della responsabilità singola. Mearly sta affermando che una persona dovrebbe avere solo cose personali, e non qualcosa come Address, che è più complesso e correlato a una persona, dovrebbe esistere come propria entità.

Si potrebbero facilmente avere operazioni specifiche per l'indirizzo come la verifica che si tratti di un indirizzo valido. Potresti non saperlo in fase di progettazione, ma l'intero concetto è quello di scomporre gli oggetti nelle loro parti più piccole in modo che qualcosa di simile sia relativamente semplice quando fatto dopo il fatto.

Aggiornamento: 1) Nella maggior parte dei casi questa convalida dell'identità viene eseguita al momento del salvataggio di un oggetto in un archivio dati. Ciò significa che esiste il codice che rappresenta la convalida dell'entità, ma esiste altrove. Di solito esiste con il codice responsabile dell'emissione del valore dell'identità. Ecco perché dichiaro che l'unicità non è rappresentata direttamente nel codice per l'entità.

2) L'affermazione corretta sarebbe che Ageè un attributo che ha un comportamento. Dovresti documentare il fatto che Age sia caricato in modo pigro in modo che uno sviluppatore che consuma quella proprietà possa prendere una decisione accurata su come consumare quella proprietà

3) di DateOfBirthsolito è un oggetto diverso; Un oggetto data che ha già operazioni predefinite su di esso. In alcune lingue l'oggetto data ha già un intero modello di dominio definito su di esso. Ad esempio in c # è possibile specificare il fuso orario, se la data è UTC, aggiungere e sottrarre le date per ottenere un periodo di tempo. Quindi la tua ipotesi sul trasloco DateOfBirthsarebbe corretta.

4) Se l'unica cosa che MyEntityfa è la delega e il coordinamento, allora no, non viola SRP. La sua unica responsabilità è delegare e coordinare ed è indicato come modello di facciata


Potresti guardare l'aggiornamento che ho fatto?
EdvRusj

Aggiornato la mia risposta
Charles Lambert,

4

Ottima domanda L'SRP non dovrebbe essere preso in modo così mediterraneo. L'identificazione / ricerca non è responsabilità dell'entità nei confronti dell'SRP. Qualcun altro è responsabile di dargli un documento di identità (vale a dire il negozio) e di cercarlo (vale a dire il repository ).

Lo scopo principale di un'entità è quello di rappresentare i concetti scoperti dal modello. L'unica differenza tra un'entità e un oggetto valore è che l'entità ha un significato oltre i suoi attributi non identificativi. Ad esempio, se una persona cambia il suo nome, rimane sempre la stessa persona, solo con un nome diverso.


1

Una volta che a un'entità viene assegnato un ID univoco, viene stabilita la sua identità e quindi presumo che tale entità non abbia bisogno di alcun comportamento per mantenere la sua identità o per aiutarla a identificarsi. Quindi, non capisco a che tipo di comportamento si riferisce l'autore (oltre alle operazioni di ricerca e abbinamento) con "comportamento essenziale per il concetto"?

Se l'identità è stabilita, sì, l'entità non ha bisogno di nient'altro per essere identificata. I comportamenti a cui fa riferimento l'autore sono comportamenti associati all'entità. Questi sono i comportamenti che modificano lo stato dell'entità. Ad esempio, Customerun'entità può avere un MakePreferredcomportamento. Il motivo per cui l'autore suggerisce di eliminare l'entità è che quando si crea inizialmente Customerun'entità, si tende a popolarla con qualsiasi attributo che si possa pensare di essere associato a un cliente. Questo è un approccio incentrato sui dati che trascura i comportamenti che alla fine portano a un modello di dominio anemico.

Per quanto riguarda lo spostamento del comportamento su altri oggetti, l'autore si riferisce specificamente agli oggetti valore. Il motivo per cui è una buona idea spostare il comportamento nei VO è perché i VO sono generalmente "più piccoli" delle entità, quindi più focalizzati. Inoltre, aspetti come l'immutabilità e la chiusura delle operazioni semplificano il ragionamento sul codice rendendolo anche più SOLIDO .

Insieme ai VO, un'entità funge da sorta di ancoraggio che coordina i vari VO che implementano il suo comportamento.

Per quanto riguarda SRP, la tua confusione non è ingiustificata. Un problema con l'implementazione stereotipata di OOP delle entità è la fusione tra identità e stato. In effetti, dal punto di vista comportamentale, l'identità non ha nulla a che fare con i comportamenti. In altre parole, l'identità di un'entità non è richiesta per nessuno dei suoi comportamenti. Esistono implementazioni in cui questa conflazione viene eliminata, come AggregateSource o un approccio funzionale che descrivo qui .

L'altro problema è che, in una certa misura, SRP può essere una misura qualitativa. Chiunque può trovare una definizione di singola responsabilità che alcune classi violano. Si può dire che la responsabilità dell'entità è quella di attuare i comportamenti richiesti da quell'entità. In questo senso, ha una sola responsabilità. Inoltre, quando un'entità delega comportamenti a VO costituenti, non viola SRP. SRP non proibisce la composizione del genere. Fa attenzione a ridurre al minimo l'accoppiamento tra gli oggetti, mantenere le interfacce il più nude possibile, ecc.

AGGIORNARE

a) Se ti capisco correttamente, stai dicendo che l'entità dovrebbe contenere solo quei comportamenti che modificano i suoi attributi (cioè il suo stato)?

Sì, anche se ci sono eccezioni ...

b) E i comportamenti che non modificano necessariamente lo stato dell'entità, ma sono comunque considerati come una caratteristica intrinseca di quell'entità (esempio: abbaiare sarebbe una caratteristica intrinseca di un'entità Cane, anche se non lo facesse modificare lo stato del cane)? Dovremmo includere questi comportamenti in un'entità o dovrebbero essere spostati su altri oggetti?

È accettabile che le entità contengano metodi di fabbrica per la creazione di istanze di entità sarebbero effettivamente entità figlio, ma dove i riferimenti agli oggetti non vengono usati per attraversare. In questo caso, l'entità figlio deve essere mantenuta dal servizio dell'applicazione. Il servizio applicativo utilizza l'entità padre per costruire l'entità figlio.

3) Supponendo che MyEntity (vedi la domanda 3. nel mio post originale) non violi SRP, potremmo dire che una responsabilità di MyEntity è tra l'altro anche composta da:

Stai guardando la responsabilità dal punto di vista dell'implementazione. Invece, considera l'entità come una sorta di scatola nera con responsabilità. Come gestisce quelli che non ti interessano come qualcuno che guarda dall'esterno. La ripartizione delle responsabilità tra i VO o anche altre entità è una preoccupazione per l'implementazione.

La citazione afferma che solo gli attributi associati all'identità dovrebbero rimanere in un'entità. Presumo autore significa che l'entità dovrebbe contenere solo quegli attributi che sono spesso usati per trovare o abbinare questa entità, mentre TUTTI gli altri attributi dovrebbero essere spostati?

Più specificamente, gli attributi che non sono richiesti per il comportamento né la ricerca non dovrebbero far parte dell'entità. Perché preoccuparsi? Inoltre, con qualcosa come il modello del modello di lettura , le entità non hanno bisogno di nulla oltre agli attributi richiesti per il comportamento.

se invece di avere Customer.Address (di tipo stringa) creiamo una proprietà Customer.Address di tipo Indirizzo, spostiamo l'attributo address in un oggetto VO associato (che è di tipo Address) o diciamo che il Cliente contiene ancora indirizzo attributo?

Sì, in effetti, non c'è differenza tra un indirizzo stringa o un indirizzo VO indirizzo.

L'autore non ha torto qui, dal momento che se assumiamo ciascuno dei tanti numeri di telefono di contatto che il Cliente appartiene solo a quel particolare Cliente, direi che questi numeri di telefono sono associati all'identità tanto quanto quando il Cliente aveva solo un numero di telefono?

Non sono al 100% sull'intento dell'autore, ma penso che stia solo descrivendo come i requisiti di ricerca delle entità possono alterare il modo in cui l'entità e i suoi VO corrispondenti sono strutture.

Fuori tema, ma ho pensato che il modello di dominio anemico derivi dal spostare il comportamento fuori da un'entità, mentre il tuo esempio sta popolando un'entità con molti attributi, il che comporterebbe un comportamento eccessivo da parte del Cliente (poiché probabilmente includeremo anche nel Cliente il comportamenti che modificano questi attributi aggiuntivi) e quindi in violazione di SRP?

Un sacco di attributi non implica un sacco di comportamento. In realtà, di solito suggerisce il contrario. Molti attributi con getter e setter, ma nessun comportamento incapsulante.


Ho fatto un aggiornamento
EdvRusj

1

TL; DR: stai pensando troppo. Tuttavia, mi sono divertito a pensarci troppo insieme a te. Quindi allacciati ....

La singola responsabilità (motivo per cambiare) di un'entità dovrebbe essere quella di identificarsi in modo univoco, in altre parole, la sua responsabilità deve essere individuabile.

No, non è vero. La sola responsabilità di un'entità è la continuità.

L'identità è una conseguenza emergente della continuità. La modellazione dell'identità come idea separabile è un'ottimizzazione delle prestazioni.

Ecco un esempio: un mecenate del ristorante offre un'auto al valletto. Un'ora dopo, un mecenate del ristorante chiede l'auto. Il cameriere dovrebbe darlo?

È facile dire che il valletto dovrebbe dare l'auto se il cliente è lo stesso. Ma cosa significa veramente? Il modo giusto per determinare che è iniziare con il patron "ora" e cercare indietro nella storia di quel patron per vedere se il dare l'auto al valletto fa parte di quella storia.

Non possiamo davvero farlo, ovviamente. Abbiamo difficoltà a tracciare la nostra storia in modo preciso, non importa la storia di qualcosa che non era con noi per tutto il tempo. Quindi, invece di usare la storia del patrono, prendiamo scorciatoie. Il cliente possiede lo stub del biglietto che ha lo stesso numero del tag attualmente legato alle chiavi? La patente di guida nel portafoglio del patron corrisponde al nome sul titolo del DMV, la foto sulla patente di guida assomiglia al volto del patron. Eccetera.

In breve: invece di controllare la storia del cliente, controlliamo lo stato corrente del cliente, per vedere se lo stato corrente è coerente con una storia che abbraccia il periodo tra l'arrivo dell'auto e la richiesta per il suo ritorno.

Quando modelliamo entità, utilizziamo un'ottimizzazione analoga. Diamo a tutte le entità le responsabilità comuni di

  1. Garantire che l'inizio della storia includa un'assegnazione di un identificatore immutabile allo stato dell'oggetto
  2. Garantire che lo stato successivo includa sempre una copia fedele dell'identificatore dello stato precedente.

Non sto descrivendo una seconda responsabilità dell'entità qui; l'entità è ancora responsabile della continuità - assicurandosi che la storia sia una narrazione coerente. Le responsabilità dell'identificatore sono solo un sottoinsieme comune a tutte le entità.

Non abbiamo ancora alcun controllo dell'unicità. Ciò non è possibile all'interno di una singola entità, poiché l'unicità richiede l'accesso allo stato di tutte le entità; dove una singola entità ha accesso solo alla propria.

Ancora una volta, controllare tutti gli identificatori ogni volta non è pratico, quindi soddisfiamo l'unicità nel modo più semplice: il codice che genera l'identificatore successivo non deve mai ripetersi.

Alla fine, ciò significa che possiamo verificare la continuità testando l'equivalenza di due diversi stati di stato in memoria, risparmiando molto fastidio nel tentativo di interrogare grafici aciclici.

Sembra anche che tu abbia confuso il principio di responsabilità singola (che è davvero una buona idea) con un principio di responsabilità atomica. La decomposizione di una responsabilità in parti più piccole e più facilmente gestibili è compatibile con SRP.


-3

Bene, dipende da come vuoi guardarlo.

Un altro modo è: "Il principio di responsabilità singola viola l'entità di dominio?"

Entrambe sono linee guida. Non esiste un "principio" da nessuna parte nella progettazione del software. Vi sono, tuttavia, buoni disegni e cattivi disegni. Entrambi questi concetti possono essere utilizzati in diversi modi, per ottenere un buon design.


Downvotes inspiegabili == fan di SRP
h bob
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.