2012-04-17 18 views
17

Sembra che ad ogni esempio trovo il modello di repository, l'implementazione è in qualche modo diversa. I seguenti sono i due esempi che trovo principalmente.Implementazione del modello di repository

interface IProductRepository 
{ 
    IQueryable<Product> FindAll(); 
} 

V'è poi un altro strato di solito che parla al repository e chiama il metodo FindAll() ed esegue tutte le operazioni, come i prodotti che trovano che iniziano con 's' o prodotti che vanno a prendere la lettera in una particolare categoria.

L'altro esempio che trovo un sacco messo tutti i metodi find nel repository

interface IProductRepository 
{ 
    IEnumerable<Product> GetProductsInCategory(int categoryId); 
    IEnumerable<Product> GetProductsStartingWith(string letter); 
    IEnumerable<PromoCode> GetProductPromoCodes(int productId); 
} 

Quale strada mi consigliate prendo? O quali sono i vantaggi/svantaggi l'uno dell'altro?

Dalla mia comprensione di aver letto http://martinfowler.com/eaaCatalog/repository.html il primo approccio sembra riflettere meglio questo?

risposta

16

Il primo è orribile. IQueryable è come un GOD object. È davvero difficile trovare un'implementazione completa al 100% (anche tra tutte le OR/Ms). Puoi esporre direttamente il tuo ORM invece di usarlo poiché probabilmente avrai un leaky abstraction layer altrimenti.

Joel dice che è meglio (testo si raggiunge dal wikipedia article):

Nell'articolo di Spolsky, si richiama l'attenzione molti esempi di astrazioni che lavorano la maggior parte del tempo, ma dove un dettaglio della complessità sottostante non può essere ignorato, e spinge in tal modo la complessità nel software che avrebbe dovuto essere semplificato l'astrazione stessa

Joels blog entry

Il secondo approccio è molto più semplice da implementare e mantenere intatta l'astrazione.

Aggiornamento

Il repository sta violando Principio Responsabilità unico, in quanto ha ottenuto due motivi per cambiare. Il primo è se l'API dei prodotti viene modificata e l'altra è se l'API PromoCode viene modificata. Si dovrebbe imho utilizzare due repository diversi, come:

interface IProductRepository 
{ 
    IEnumerable<Product> FindForCategory(int categoryId); 
    IEnumerable<Product> FindAllStartingWith(string letter); 
} 

interface IPromoCodeRepository 
{ 
    IEnumerable<PromoCode> FindForProduct(int productId); 
} 

le cose sono cambiate:

  • tendo a iniziare la modalità con Find quando diversi elementi vengono restituiti e Get se un singolo elemento viene restituito.
  • Nomi di metodo più brevi = più facili da leggere.
  • Responsabilità unica. È più facile dire quali sono le classi che utilizzano i repository per le dipendenze.

Piccole interfacce ben definite facilitano l'individuazione delle violazioni dei principi SOLID in quanto le classi che infrangono i principi tendono ad avere costruttori gonfiati.

+0

Grazie per la risposta. Pensavo che fossi destinato a raggruppare per aggregati? Dovrei avere un repository per entità? Anche qui c'è un esempio del primo esempio che ho fornito.http: // StackOverflow.it/questions/5049363/difference-between-repository-and-service-layer – Scott

+0

Per aggregato. Non potevo dire che i codici promozionali erano solo per prodotti. (poiché è stato chiamato 'GetProductPromos' e non solo' GetPromos') – jgauffin

+0

non ha la tua prima interfaccia, anche 2 motivi per cambiare? primo: cambiare il metodo FindForCategory per modificare il prodotto restituito predefinito se non trova alcun prodotto passando categoryId e in secondo luogo cambiando FindAllStartingWith per applicare alcuni filtri predefiniti? – Masoud

0

Personalmente suggerirei di utilizzare il secondo esempio, in questo modo si incapsula la logica di ricerca in un unico punto e l'intento del chiamante è chiaramente definito dal nome del metodo che stanno chiamando. Se segui il primo esempio, il tuo codice di query si diffonderà nell'applicazione e finirai per duplicare le query.

0

Si consiglia di evitare duplicazioni. Questo è il primo obiettivo. Se hai una logica che trova prodotti che iniziano con una lettera in più punti, allora è un caso speciale e vale la pena di essere estratto per separare il metodo (dà anche una buona descrizione per il tuo caso specifico). Il codice senza duplicazioni è molto più facile da cambiare, capire e mantenere.

Così, ho tendono ad avere un metodo generico di ricerca con IQueryable e insieme di metodi che hanno usato più di una volta:

interface IRepository<T> 
{ 
    IQueryable<T> FindAll(); 
} 

interface IProductRepository : IRepository<Product> 
{ 
    IEnumerable<Product> GetProductsInCategory(int categoryId); 
    IEnumerable<Product> GetProductsStartingWith(string letter); 
    IEnumerable<PromoCode> GetProductPromoCodes(int productId); 
} 

Considera anche unit test. I metodi specifici sono molto più facili da deridere di IQueryable.

0

In realtà penso che il primo sia migliore. Suppongo la mia decisione sui seguenti fattori:

  1. Se la struttura del prodotto otterrà refactoring:

    • approccio IQueryable - avete solo bisogno di modificare i metodi di chiamata in codice.
    • IEnumerables: è necessario modificare anche i nomi dei metodi.

  2. Se molti in procinto di derivare le interfacce che si desidera iterare in maniera polimorfa:

    • approccio IQueryable - beneficio di nomi di metodo generico uniformi
    • IEnumerables - alcuni nomi non potrebbe descrivi il metodo che ti serve

  3. Elasticness

    • approccio IQueryable - facile da dividere in approccio IEnumerables.
    • Approccio IEnumerables: difficile riconvertire con l'approccio IQueryable.

Quindi, vi suggerisco di iniziare con IQueryable come scelta di default e come si procede con il codice si può sempre cambiare le IEnumerables più specifici approccio che si ha bisogno.

1

Il consenso sta costruendo: seconda opzione fino in fondo. A parte la logica delle query che si diffonde dappertutto con IQueryable, la difficoltà di implementarlo correttamente è molto difficile da provare e prendere in giro.

Problemi correlati