2015-01-23 16 views
10

Sto facendo qualche ricerca sul principio SOLID e ho trovato alcuni problemi nelle implementazioni del modello di repository. Ho intenzione di spiegare ogni singolo problema, per favore correggimi se sbaglio.Il modello di deposito segue i principi SOLID?

Problema 1

Repository modello Breaks principio responsabilità singola (S)

Let dire che abbiamo un interfaccia che definiscono

public interface IRepository<T> where T: IEntity 
{ 

    IEnumerable<T> List { get; } 
    void Add(T entity); 
    void Delete(T entity); 
    void Update(T entity); 
    T FindById(int Id); 
} 

Chiaramente viola il principio della responsabilità unica perché w Quando implementiamo questa interfaccia, in una singola classe mettiamo sia Command and Query entrambi. e questo non è previsto.

Problema 2

Repository Motivo breaks Interface principio della segregazione (I)

Say Abbiamo 2 Esecuzione del sopra Interface.

prima implementazione

CustomerRepository : IRepository<Customer> 
{ 
    //All Implementation 
} 

seconda implementazione

ProductRepository : IRepository<Product> 
{ 
    //All Implementation except Delete Method. So Delete Method Will be 
    void Delete (Product product){ 
     throw Not Implement Exception! 
    } 
} 

E come da ISP "Nessun cliente dovrebbe essere costretto a dipendere da metodi che non utilizza." Quindi abbiamo visto chiaramente che viola anche l'ISP.

Quindi, la mia comprensione è che il pattern Repository non segue il principal SOLID. Cosa ne pensi? Perché dovremmo scegliere questo tipo di modello che viola il preside? Hai bisogno della tua opinione.

risposta

21

Chiaramente viola il principio di responsabilità singola perché quando implementiamo questa interfaccia, in una singola classe mettiamo sia Command and Query entrambi. e questo non è previsto.

Questo non è il Principio di responsabilità singola. SRP significa che la classe dovrebbe avere una preoccupazione primaria. La preoccupazione principale di un repository è di "mediare tra il dominio e i livelli di mapping dei dati utilizzando un'interfaccia simile a una raccolta per l'accesso agli oggetti di dominio" (Fowler). Questo è ciò che fa questa classe.

Repository modello Breaks interfaccia principio della segregazione

Se questo vi da fastidio, quindi è sufficiente fornire un'altra interfaccia che non include il metodo che non hai intenzione di implementare. Personalmente non lo farei, però; sono un sacco di interfacce extra per un vantaggio marginale e ingombra l'API inutilmente.A NotImplementedException è molto auto-esplicativo.

Scoprirai che ci sono un sacco di regole, leggi o principi nel calcolo che hanno eccezioni, e alcuni che sono completamente sbagliati. Abbracciare l'ambiguità, imparare a scrivere software da una prospettiva più pratica e smettere di pensare al design del software in termini assoluti.

+0

Grazie @Robert, molto utile. –

+0

@AnkitSarkar L''Ipository 'è un anti-pattern di per sé quando non viene utilizzato corretamente. http://codebetter.com/gregyoung/2009/01/16/ddd-the-generic-repository/ – plalx

4

Chiaramente viola il principio unico responsabilità

E 'chiaro solo se si dispone di una definizione molto ristretta di ciò che l'SRP è. Il fatto è che SOLID viola SOLID. I principi stessi si contraddicono. SRP è in disaccordo con DRY, dal momento che spesso devi ripetere te stesso per separare adeguatamente le preoccupazioni. LSP è in contrasto con ISP in alcune situazioni. L'OCP spesso è in conflitto con DRY e SRP. Questi principi non sono qui regole rigide e veloci, ma per guidarti ... cerca di aderire a loro, ma non trattarli come leggi che non possono essere infranti.

Oltre a ciò, si confonde il modello di architettura del repository, con uno schema di implementazione del repository generico molto specifico. Si noti che un repository generico è diverso da un repository concreto. Né è necessario che un repository implementa i metodi che menzioni.

Sì, è possibile separare il comando e la query come due preoccupazioni distinte, ma non è necessario che lo si faccia per rendere ciascuna una singola responsabilità. Command Query La separazione è un buon principio, ma non qualcosa che è coperto da SOLID, e certamente non vi è consenso sul fatto che separare o meno le preoccupazioni rientri nel rispetto delle diverse responsabilità. Sono più simili a diversi aspetti della stessa responsabilità. Potresti portarlo a un livello ridicolo se lo desideri e sostenere che l'aggiornamento è una responsabilità diversa dall'eliminazione o che la query per id è una responsabilità diversa dall'interrogazione per tipo o altro. Ad un certo punto devi disegnare le linee e le scatole, e per molte persone "leggere e scrivere un'entità" è una singola responsabilità.

Repository modello Breaks interfaccia principio della segregazione

In primo luogo, si sta confondendo Liskov Sostituzione principale con interfaccia segregazione Principio. LSP è ciò che viene violato dal tuo esempio.

Come detto in precedenza, non è necessario che Repository implementi alcun insieme di metodi specifici, ad eccezione di una "interfaccia di raccolta". Infatti, sarebbe perfettamente accettabile per attuare in questo modo:

public interface IRepository<T> where...[...] {IEnumerable<T> List { get; }} 
public interface CustRepository : IRepository<Customer>, IRepoAdd, IRepoUpdate, IRepoDelete, IRepoFind {} 

Ora Opzionalmente può implementare qualsiasi degli altri membri senza rompere LSP, anche se è piuttosto stupido attuazione e uno certamente non attuare solo per evitare di rompere LSP.

Il fatto è che probabilmente non c'è una buona ragione per cui si desidera un repository senza eliminare. L'unica ragione possibile che posso pensare sarebbe un repository di sola lettura, che definirei un'interfaccia separata per l'utilizzo di un'interfaccia di raccolta di sola lettura.

2

Io stesso utilizzo il pattern Repository e ho utilizzato il pattern per assicurarmi che tutte le interfacce richieste siano implementate. Per questo ho creato interfacce separate per tutte le azioni (IEntityCreator, IEntityReader, IEntityUpdater, IEntityRemover) e ho fatto in modo che il repository ereditasse tutte queste interfacce. In questo modo posso implementare tutti i metodi in una classe concreta e utilizzare ancora tutte le interfacce separatamente. Non vedo un motivo per affermare che il modello di deposito viola i principi SOLID.Hai solo bisogno di definire correttamente la "responsabilità" del repository: la responsabilità del Repository è quella di facilitare ogni accesso ai dati di tipo T. Questo è tutto quello che c'è da dire. Come detto sopra, ho anche un'interfaccia di repository di sola lettura denominata ReferenceRepository<T> che include solo l'interfaccia IEntityReader<T>. Di seguito sono descritte tutte le interfacce per la copia veloce :) Inoltre ho creato anche alcune classi concrete, inclusa la memorizzazione nella cache e/o la registrazione. Questo è quello di incorporare ulteriori azioni richieste come indicato dallo I in SOLID. Il tipo IEntity viene utilizzato come interfaccia marker per consentire solo le entità e non qualche altro tipo di oggetto (devi iniziare da qualche parte).

/// <summary> 
/// This interface defines all properties and methods common to all Entity Creators. 
/// </summary> 
/// <typeparam name="TEntity">The type of the entity.</typeparam> 
public interface IEntityCreator<TEntity> 
    where TEntity : IEntity 
{ 
    #region Methods 
    /// <summary> 
    /// Create a new instance of <see cref="TEntity"/> 
    /// </summary> 
    /// <returns></returns> 
    TEntity Create(); 
    #endregion 
} 

/// <summary> 
/// This interface defines all properties and methods common to all Entity Readers. 
/// </summary> 
/// <typeparam name="TEntity">The type of the entity.</typeparam> 
public interface IEntityReader<TEntity> 
    where TEntity : IEntity 
{ 
    #region Methods 
    /// <summary> 
    /// Get all entities in the data store. 
    /// </summary> 
    /// <returns></returns> 
    IEnumerable<TEntity> GetAll(); 

    /// <summary> 
    /// Find all entities that match the expression 
    /// </summary> 
    /// <param name="whereExpression">exprssion used to filter the results.</param> 
    /// <returns></returns> 
    IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> whereExpression); 
    #endregion 
} 

/// <summary> 
/// This interface defines all properties and methods common to all Entity Updaters. 
/// </summary> 
/// <typeparam name="TEntity">The type of the entity.</typeparam> 
public interface IEntityUpdater<TEntity> 
    where TEntity : IEntity 
{ 
    #region Methods 
    /// <summary> 
    /// Save an entity in the data store 
    /// </summary> 
    /// <param name="entity">The entity to save</param> 
    void Save(TEntity entity); 
    #endregion 
} 

/// <summary> 
/// This interface defines all properties and methods common to all Entity removers. 
/// </summary> 
/// <typeparam name="TEntity">The type of the entity.</typeparam> 
public interface IEntityRemover<TEntity> 
    where TEntity : IEntity 
{ 
    /// <summary> 
    /// Delete an entity from the data store. 
    /// </summary> 
    /// <param name="entity">The entity to delete</param> 
    void Delete(TEntity entity); 

    /// <summary> 
    /// Deletes all entities that match the specified where expression. 
    /// </summary> 
    /// <param name="whereExpression">The where expression.</param> 
    void Delete(Expression<Func<TEntity, bool>> whereExpression); 
} 

/// <summary> 
/// This interface defines all properties and methods common to all Repositories. 
/// </summary> 
public interface IRepository { } 

/// <summary> 
/// This interface defines all properties and methods common to all Read-Only repositories. 
/// </summary> 
/// <typeparam name="TEntity">The type of the entity.</typeparam> 
public interface IReferenceRepository<TEntity> : IRepository, IEntityReader<TEntity> 
    where TEntity : IEntity 
{ 

} 

/// <summary> 
/// This interface defines all properties and methods common to all Read-Write Repositories. 
/// </summary> 
public interface IRepository<TEntity> : IReferenceRepository<TEntity>, IEntityCreator<TEntity>, 
    IEntityUpdater<TEntity>, IEntityRemover<TEntity> 
    where TEntity : IEntity 
{ 

} 
+0

PS. Ho aggiunto alcune altre funzionalità nelle interfacce ereditate per le entità aziendali (entità che hanno in ID), ma ho lasciato questo fuori per creare una soluzione più generale –