Le iniezioni di dipendenza nelle entit sono considerate contro SRP?

3

Ho chiesto una domanda su Stackoverflow in precedenza e qualcuno mi ha indirizzato a un post precedente di lui, afferma che l'iniezione di una dipendenza in un'entità viola il Principio di Responsabilità Unica.

Per salvare l'utente facendo clic sul link e fornire un contesto, ecco l'entità in discussione

public class MyEntity
{
     private readonly IValidatorFactory _validatorFactory;
     public MyEntity(IValidatorFactory validatorFactory)
     {
         _validatorFactory = validatorFactory;
     }

     //Entity Properties Removed For Clarity

     public void Validate()
     {
         if(_validatorFactory == null)
         {
                throw new ArgumentNullException("Validator Factory was null, cannot validate this item");
         }

         var validator = _validatorFactory.GetValidator(this.ItemTypeId);
         valid = validator.Validate();

     }
}

Avrei pensato che avere l'IValidatorFactory che estraeva il tipo di convalida dall'entità era esattamente ciò di cui trattava l'SRP. Trasmette i problemi di convalida ad un'altra classe. Aderisce inoltre al principio Open / Closed e consente il polimorfismo / estensione anziché modificare il codice client esistente.

Per avere un valore reale qui, l'entità deve richiedere diversi tipi di convalida in base a un valore, che è ciò che accade quando this.ItemTypeId viene utilizzato per ottenere il riferimento IValidator corretto.

Questo è un completo fraintendimento dei principal SOLID?

// modifica

La domanda contrassegnata come possibile duplicato in realtà non risponde Questo. Le entità sono classi e dovrebbero incapsulare la propria convalida, altrimenti si sta andando verso un territorio anti modello di dominio anemico. Sto chiedendo specificamente se avendo una dipendenza che controlla la convalida, sto andando contro i principal SOLID.

    
posta James 23.02.2016 - 15:25
fonte

3 risposte

5

Quindi non c'è niente qui che mi sembra una violazione SRP. L'entità ha proprietà e usa un helper per sapere se è valido. Questi potrebbero essere meglio separati (devi avere un validatore per testare l'entità), ma lasciare che un'entità sappia se è valido o meno va bene.

Avere una fabbrica fornisce validatori è una bella responsabilità unica. Avere un validatore che funziona contro una singola entità è una bella singola responsabilità.

Ora, tutto ciò che ha detto, è un po 'puzzolente. Avere un ID che mappa in maniera implicita un tipo è un accoppiamento stretto senza grandi vantaggi. Avere una factory for validators quando ne hai quasi sempre uno è YAGNI e over-engineering. Controllare la fabbrica in validate piuttosto che nel costruttore è un po 'di schivata, dal momento che non stai cogliendo il bug dove accade.

    
risposta data 23.02.2016 - 16:07
fonte
2

SRP è un principio notoriamente difficile da definire. A quanto ho capito, quello che stai dicendo è che l'entità in realtà non sa come convalidare se stessa, quindi la convalida non è una delle sue responsabilità. Quello che Mark sta dicendo è che la responsabilità di un'entità è quella di archiviare i dati e nient'altro, quindi avere un metodo Validate, specialmente uno che richiede l'iniezione di una fabbrica, ha un cattivo odore.

Questo è il conflitto di cui parla Martin Fowler nel capitolo Smelling del codice di Refactoring . Suggerisce che è meglio considerare una responsabilità "una ragione per cambiare". vale a dire. Ogni classe dovrebbe avere una sola ragione per cambiare.

In quel contesto, Mark ha ragione. Ora hai più motivi per cambiare questa entità ... se la struttura dei dati cambia; se la fabbrica ha bisogno di informazioni diverse per selezionare un validatore; se il validatore necessita di ulteriori informazioni per la convalida.

Questa non è una gran violazione, in verità. Alcuni di questi cambiamenti sono abbastanza improbabili. Ma se un'entità può validare se stessa, perché non memorizzare anche se stessa? È possibile iniettare facilmente un oggetto di database insieme alla fabbrica del validatore. Chiediti: quando diventa troppo?

Inoltre, considera il codice chiamante. In definitiva, è più pulito dire entity.Validate() rispetto a validator.Validate(entity) ? E come stai generando la tua entità? Stai passando valori di proprietà nel costruttore, insieme al Validator Factory, o è mutabile? Questa classe chiamante sembra che potrebbe essere molto incline a cambiare, per molte ragioni.

Che va bene se ci sono dei benefici. Ma non posso, personalmente, immaginare cosa potrebbero essere questi.

EDIT: Dopo un po 'di riflessione, mi viene in mente che questa è anche la discussione intorno a Modelli di dominio anemico , su cui anche Martin Fowler ha molto per dire .

Quindi ci sono ben due aspetti di questo argomento. Tuttavia, se stai adottando l'approccio del modello grasso, hai un problema diverso. Dovresti quindi avere un diverso tipo di entità per ogni ItemTypeId, come suggerito da Euforic.

    
risposta data 23.02.2016 - 16:19
fonte
2

I would have thought that having the IValidatorFactory abstracting the validation type from the entity was exactly what the SRP was about.

No. Niente affatto.

SRP parla di una classe che si attacca al suo scopo e ci sono due aspetti. Frist, facendo quello che dovrebbe fare e in secondo luogo, non facendo quello che non dovrebbe fare. Ho sempre pensato alla convalida come prima.

Convalidare il proprio stato è certamente la responsabilità della classe data. Perché? Una classe ben fatta nasconde lo stato ed espone le funzionalità. Quindi una classe usa le sue proprietà per eseguire funzioni. Non espone le proprietà e lascia, o forza, il codice client per eseguire tali funzioni. E quindi mi aspetto di vedere la convalida della proprietà in quella classe. Inoltre, data una composizione di classi ogni classe farebbe la sua specifica convalida e mi aspetterei di vedere la classe che contiene la convalida dell'interazione dell'oggetto e invitare le classi partecipanti a fare la loro parte.

Il pattern factory è generalmente utilizzato per isolare / consolidare la costruzione di una struttura di classe / classe complessa - "grafici oggetto", alcuni direbbero. E in genere lo vedo quando costruisco variazioni di sottoclasse su una base. Ma di solito non "fabbrica" quei bit fondamentali di una classe che definisce cosa sia quella classe. E una classe è in parte le sue proprietà e la loro validazione.

    
risposta data 23.02.2016 - 16:08
fonte

Leggi altre domande sui tag