Спецификация (шаблон проектирования)
Из Википедии, бесплатной энциклопедии

«Спецификация» в программировании — это шаблон проектирования, посредством которого представление правил бизнес-логики может быть преобразовано в виде цепочки объектов, связанных операциями булевой логики.
Этот шаблон выделяет такие спецификации (правила) в бизнес-логике, которые подходят для «сцепления» с другими. Объект бизнес-логики наследует свою функциональность от абстрактного агрегирующего класса CompositeSpecification, который содержит всего один метод IsSatisfiedBy, возвращающий булево значение. После инстанцирования объект объединяется в цепочку с другими объектами. В результате, не теряя гибкости в настройке бизнес-логики, мы можем с лёгкостью добавлять новые правила.
Примеры кода
[править | править код]C#
[править | править код] public interface ISpecification { bool IsSatisfiedBy(object candidate); ISpecification And(ISpecification other); ISpecification Or(ISpecification other); ISpecification Not(); } public abstract class CompositeSpecification : ISpecification { public abstract bool IsSatisfiedBy(object candidate); public ISpecification And(ISpecification other) { return new AndSpecification(this, other); } public ISpecification Or(ISpecification other) { return new OrSpecification(this, other); } public ISpecification Not() { return new NotSpecification(this); } } public class AndSpecification : CompositeSpecification { private ISpecification One; private ISpecification Other; public AndSpecification(ISpecification x, ISpecification y) { One = x; Other = y; } public override bool IsSatisfiedBy(object candidate) { return One.IsSatisfiedBy(candidate) && Other.IsSatisfiedBy(candidate); } } public class OrSpecification : CompositeSpecification { private ISpecification One; private ISpecification Other; public OrSpecification(ISpecification x, ISpecification y) { One = x; Other = y; } public override bool IsSatisfiedBy(object candidate) { return One.IsSatisfiedBy(candidate) || Other.IsSatisfiedBy(candidate); } } public class NotSpecification : CompositeSpecification { private ISpecification Wrapped; public NotSpecification(ISpecification x) { Wrapped = x; } public override bool IsSatisfiedBy(object candidate) { return !Wrapped.IsSatisfiedBy(candidate); } }
C# 3.0, упрощённый через шаблоны и методы расширения
[править | править код] public interface ISpecification<TEntity> { bool IsSatisfiedBy(TEntity entity); } internal class AndSpecification<TEntity> : ISpecification<TEntity> { private readonly ISpecification<TEntity> _spec1; private readonly ISpecification<TEntity> _spec2; protected ISpecification<TEntity> Spec1 { get { return _spec1; } } protected ISpecification<TEntity> Spec2 { get { return _spec2; } } internal AndSpecification(ISpecification<TEntity> spec1, ISpecification<TEntity> spec2) { if (spec1 == null) throw new ArgumentNullException("spec1"); if (spec2 == null) throw new ArgumentNullException("spec2"); _spec1 = spec1; _spec2 = spec2; } public bool IsSatisfiedBy(TEntity candidate) { return Spec1.IsSatisfiedBy(candidate) && Spec2.IsSatisfiedBy(candidate); } } internal class OrSpecification<TEntity> : ISpecification<TEntity> { private readonly ISpecification<TEntity> _spec1; private readonly ISpecification<TEntity> _spec2; protected ISpecification<TEntity> Spec1 { get { return _spec1; } } protected ISpecification<TEntity> Spec2 { get { return _spec2; } } internal OrSpecification(ISpecification<TEntity> spec1, ISpecification<TEntity> spec2) { if (spec1 == null) throw new ArgumentNullException("spec1"); if (spec2 == null) throw new ArgumentNullException("spec2"); _spec1 = spec1; _spec2 = spec2; } public bool IsSatisfiedBy(TEntity candidate) { return Spec1.IsSatisfiedBy(candidate) || Spec2.IsSatisfiedBy(candidate); } } internal class NotSpecification<TEntity> : ISpecification<TEntity> { private readonly ISpecification<TEntity> _wrapped; protected ISpecification<TEntity> Wrapped { get { return _wrapped; } } internal NotSpecification(ISpecification<TEntity> spec) { if (spec == null) { throw new ArgumentNullException("spec"); } _wrapped = spec; } public bool IsSatisfiedBy(TEntity candidate) { return !Wrapped.IsSatisfiedBy(candidate); } } public static class ExtensionMethods { public static ISpecification<TEntity> And<TEntity>(this ISpecification<TEntity> spec1, ISpecification<TEntity> spec2) { return new AndSpecification<TEntity>(spec1, spec2); } public static ISpecification<TEntity> Or<TEntity>(this ISpecification<TEntity> spec1, ISpecification<TEntity> spec2) { return new OrSpecification<TEntity>(spec1, spec2); } public static ISpecification<TEntity> Not<TEntity>(this ISpecification<TEntity> spec) { return new NotSpecification<TEntity>(spec); } }
Пример использования
[править | править код]В следующем примере мы проверяем счета и отсылаем их в агентство по сбору платежей, если: они просрочены, ещё не были отправлены в агентство и покупателю было выслано предупреждение. Этот пример показывает, как правила «сцепляются» друг с другом.
Пример опирается на три спецификации: OverdueSpecification, которая верна, если счёт был выставлен более чем 30 дней назад, NoticeSentSpecification, которая верна, если покупателю было отослано 3 предупреждения, и InCollectionSpecification, проверяющая, что счёт ещё не отсылался в агентство по сбору платежей. Реализация этих классов не так важна.
Используя эти три спецификации, мы создаём новое правило SendToCollection, которое верно, если выполняются все три условия, описанные в предыдущем абзаце.
OverDueSpecification OverDue = new OverDueSpecification(); NoticeSentSpecification NoticeSent = new NoticeSentSpecification(); InCollectionSpecification InCollection = new InCollectionSpecification(); // пример "сцепления" правил ISpecification<Invoice> SendToCollection = OverDue.And(NoticeSent).And(InCollection.Not()); InvoiceCollection = Service.GetInvoices(); foreach (Invoice currentInvoice in InvoiceCollection) { if (SendToCollection.IsSatisfiedBy(currentInvoice)) { currentInvoice.SendToCollection(); } }
Примечания
[править | править код]Литература
[править | править код]- Evans, E: «Domain-Driven Design.», page 224. Addison-Wesley, 2004.
Ссылки
[править | править код]- Specifications by Eric Evans and Martin Fowler
- The Specification Pattern: A Primer by Matt Berther
- The Specification Pattern: A Four Part Introduction using VB.Net by Richard Dalton
- specification pattern in flash actionscript 3 by Rolf Vreijdenberger
![]() | Для улучшения этой статьи желательно: |