Сравнение шаблона спецификаций, FuncПредикаты, Трубы и Фильтры - PullRequest
7 голосов
/ 27 августа 2010

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

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

Рассмотрим следующий интерфейсный контракт для уровня обслуживания:

public interface IFooDataService
{
   ICollection<Foo> GetFoosBySpecification(Specification<Foo> specification);
   ICollection<Foo> GetFooByPredicate(Func<Foo,bool> predicate);
   ICollection<Foo> GetFooBySearchArgs(FooSearchArgs searchArgs);
}

Итак, некоторые начальные точки:

  • Все три возвращают коллекцию объектов Foo
  • Все три принимают один единственный аргумент
  • Спецификация метода ограничивает доступ к определенным требованиям
  • Метод предикатов практически не имеет ограничений
  • Метод поиска args ограничивает доступ к определенным требованиям

Теперь перейдем к реализации:

public ICollection<Foo> GetFoosBySpecification(Specification<Foo> specification)
{
    return fooDataRepository
            .Find()
            .Where(f => specification.IsSatisfiedBy(f))
            .ToList();
}

public ICollection<Foo> GetFooByPredicate(Func<Foo, bool> predicate)
{
    return fooDataRepository
            .Find()
            .Where(predicate)
            .ToList();
}

public ICollection<Foo> GetFooBySearchArgs(FooSearchArgs searchArgs)
{
    return fooDataRepository
            .Find()
            .WhereMeetsSearchCriteria(searchArgs)
            .ToList();
}

Баллы за выполнение:

  • Все три чрезвычайно просты в реализации (одна строка цепочечного кода)
  • Фильтры спецификаций и поиска отфильтрованы, реализованы извне.
  • Метод поиска аргументов просто использует метод расширения IEnumerable для проверки аргументов

Итак, как говорится, при каких условиях вы бы использовали одну из трех вышеуказанных техник?

Мои мысли по шаблону спецификации:

  • Приятно, что он изолирует требования бизнеса / домена в повторно используемые компоненты
  • Очень легко читается, код говорит по-английски
  • Довольно много задействованного кода (интерфейсы, абстрактные классы). Если бы я использовал это, я бы поместил абстракции в общую сборку (чтобы в моем решении не было набора статических файлов).
  • Легко изменить требования, изменив только спецификацию, а не уровень обслуживания.
  • Высочайшая тестируемость логики домена (спецификации)

Мои мысли о методах расширения (трубы и фильтры):

  • «Взвешенный» в логике, но все равно приводит к той же простоте.
  • Изолировать логику запроса от уровня обслуживания до статических методов
  • По-прежнему требуется «отражение» сортировки (проверка предоставленных аргументов поиска и создание запроса)
  • Позволяет сначала кодировать архитектуру (репозиторий, сервисный уровень), не думая о конкретных бизнес-требованиях (что удобно в определенных сценариях)

Мои мысли о методе предиката:

  • Может использоваться там, где требуется грубый контроль над запросами.
  • Подходит для небольших проектов, где спецификации могут переусердствовать

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

Но для приложения, которое является «запуском», то есть требования будут развиваться со временем и иметь множество способов извлечения данных без сложной проверки, я бы использовал методы Pipes и Filters.

Что вы думаете? У кого-нибудь из вас были проблемы с любым из вышеперечисленных методов? Любые рекомендации?

О том, чтобы начать новый проект, поэтому подобные соображения имеют решающее значение.

Спасибо за помощь.

РЕДАКТИРОВАТЬ для уточнения по спецификации

Вот то же самое использование шаблона спецификации.

Specification<Foo> someSpec; // Specification is an abstract class, implementing ISpecification<TEntity> members (And, Or, Not, IsSatisfiedBy).
someSpec = new AllFoosMustHaveABarSpecification(); // Simple class which inherits from Specification<Foo> class, overriding abstract method "IsSatisfiedBy" - which provides the actual business logic.    
ICollection<Foo> foos = fooDataService.GetFoosBySpecification(someSpec);

Ответы [ 2 ]

2 голосов
/ 27 августа 2010

Из моего небольшого опыта:

  1. Требования пользователей всегда меняются, и я не знаю, почему мой начальник всегда допускает такие изменения.Так что +1 к спецификации
  2. Программист здесь больше похож на «работника физического труда», чем на «работника умственного труда».Вы знаете .. тот, кто печатает весь день.Используя Спецификацию, я могу убедиться, что все "печатают".Это подтверждается характером моего проекта.Для одной и той же цели требуется много разных реализаций.Не спрашивайте меня, почему.
  3. Используйте шаблон проектирования, который дает вам высочайшую модульность, гибкость и, конечно, тестируемость.Вот небольшая история.
    В один прекрасный день мой приятель сказал мне, что он написал класс, который позволил нам рассчитать 32 способа вычисления X. И он уже все это реализовал.Хохо, это было такое героическое программирование, я думаю.Он провел несколько недель, делая это посреди ночи.Он считал, что он хороший программист, поэтому он настаивал на том, чтобы каждый использовал его шедевр.
    В то время мы не интересовались модульным тестированием, поэтому мы использовали его шедевр.Что случилось потом?Код все время падал.Ну, с того времени я понял, насколько важны модульный тест и модульность.
0 голосов
/ 27 августа 2010

Ну, во-первых, я бы написал метод Predicate, даже если он используется только как частная реализация для двух других:

private ICollection<Foo> GetFoosBySpecification(Specification<Foo> spec) 
{ 
    return GetFooByPredicate(f => spec.IsSatisfiedBy(f));
} 

Функция аргумента поиска будет похожа на одну строку.

Кроме того, я действительно не могу ничего сказать абстрактно. Мне нужно больше узнать о структуре данных, чтобы решить, как лучше их искать.

...