Я занимаюсь исследованиями и разработками, и поэтому изучаю шаблоны проектирования. Недавно я читал шаблон спецификаций и получил ссылку на эту замечательную статью.
Я был заинтригован простотой и чистотой кода, но я начал проводить некоторые сравнения с реализацией той же чистоты, используя другие методы.
Рассмотрим следующий интерфейсный контракт для уровня обслуживания:
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);