2012-04-17 3 views
17

Кажется, что каждый пример, который я нахожу в шаблоне репозитория, в какой-то мере отличается. Ниже приведены два примера, которые я в основном нахожу.Реализация шаблона хранилища

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

Существует то, как правило, еще один слой, который говорит в хранилище и вызывает метод FindAll() и выполняет любые операции, такие как поиск продуктов, начинающихся с буквы «S» или выборки продукции в конкретной категории.

Другой пример, который я нашел много положить все методы находят в хранилище

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

Какой путь вы посоветуете взять? Или каковы преимущества/недостатки друг от друга?

С моей точки зрения, прочитав http://martinfowler.com/eaaCatalog/repository.html, первый подход лучше всего отражает это?

ответ

16

Первый ужасный. IQueryable - это как GOD object. На самом деле трудно найти 100% полную реализацию (даже среди всех OR/M). Вы можете разоблачить свой ORM напрямую, а не использовать его, так как вы, вероятно, получите leaky abstraction layer.

Джоэл говорит, что лучше (текст из wikipedia article):

В статье Сполски, он обращает внимание на множество примеров абстракций, которые работают большую часть времени, но когда деталь базовой сложности не может быть проигнорированы, и, таким образом, сводит сложность в программное обеспечение, которое должно было быть упрощена путем самой абстракции

Joels blog entry

Второй подход намного проще реализовать и сохранить целостность абстракции.

Update

Ваше хранилище нарушает единый принцип ответственности, поскольку он получил две причины для изменения. Во-первых, если API-интерфейс продуктов изменен, а другой - если API-интерфейс PromoCode изменен. Вы должны IMHO использовать два различных репозиториев, как:

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

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

изменившихся вещи:

  • Я, как правило, начинаются методы с Find, когда несколько пунктов возвращаются и Get если один элемент возвращается.
  • Более короткие имена методов = легче читать.
  • Единая ответственность. Легче сказать, какие классы, которые используют репозитории, имеют зависимости.

Небольшие четко определенные интерфейсы облегчают выявление нарушений принципов SOLID, поскольку классы, которые нарушают принципы, имеют тенденцию приобретать раздутые конструкторы.

+0

Спасибо за ответ. Я думал, что тебе нужно группировать по агрегатам? Должен ли я иметь один репозиторий для каждого объекта? Также здесь приведен пример первого примера. Http: // stackoverflow.com/questions/5049363/Уровень разницы между репозиториями и услугами – Scott

+0

На совокупность. Я не мог сказать, что промо-коды предназначены только для продуктов. (поскольку он был назван «GetProductPromos», а не только «GetPromos») – jgauffin

+0

не имеет вашего первого интерфейса, а также 2 причины для изменения? во-первых: изменение метода FindForCategory для изменения возвращаемого по умолчанию продукта, если он не находит какой-либо продукт по переданной категорииId, и вторые изменения FindAllStartingWith для применения некоторых фильтров по умолчанию? – Masoud

0

Лично я бы предложил использовать второй пример, таким образом, вы инкапсулируете логику поиска в одном месте, а цель вызывающего лица четко определяется именем метода, который они вызывают. Если вы перейдете к первому примеру, ваш код запроса будет протекать по всему вашему приложению, и вы получите дублирование запросов.

0

Я рекомендую избегать дублирования. Это первая цель. Если у вас есть логика, которая находит продукты, начинающиеся с буквы в нескольких местах, то это особый случай, и его ценность извлекается для раздельного метода (также он дает хорошее описание для вашего конкретного случая). Код без дублирования намного проще изменить, понять и поддерживать.

Итак, я, как правило, имеют один общий метод поиска с IQueryable и набор методов, которые используются более чем один раз:

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); 
} 

Рассмотрим также блок-тестирования. Конкретные методы намного проще высмеивать, чем IQueryable.

0

Я на самом деле думаю, что 1-й лучше. Я предполагаю, что мое решение о следующих факторах:

  1. Если структура продукта будет получить рефакторинга:

    • IQueryable подход - вам нужно всего лишь изменить методы, требующие в коде.
    • IEnumerables - вам также нужно изменить имена методов.

  2. Если многие собираются вывести интерфейсы, которые вы хотите, чтобы перебирать в полиморфных образом:

    • IQueryable подход - выгоды от имен методов родовых равномерных
    • IEnumerables - некоторые названия не может описать метод, который вам нужен.

  3. Elasticness

    • IQueryable подход - легко разделить на IEnumerables подход.
    • Подход IEnumerables - трудно преобразовать в подход IQueryable.

Итак, я предлагаю вам начать с IQueryable как выбор по умолчанию, и по мере продвижения с вашим кодом, вы всегда можете перейти к более конкретным IEnumerables приближающихся вам нужно.

1

Консенсус строится: вторая опция полностью. Помимо логики запросов, протекающей повсюду с помощью IQueryable, трудности с ее реализацией в правильном направлении, очень сложно тестировать и издеваться.

Смежные вопросы