Спецификация (шаблон проектирования)

Из Википедии, бесплатной энциклопедии

Шаблон проектирования «Спецификация» в виде UML диаграммы

«Спецификация» в программировании  — это шаблон проектирования, посредством которого представление правил бизнес-логики может быть преобразовано в виде цепочки объектов, связанных операциями булевой логики.

Этот шаблон выделяет такие спецификации (правила) в бизнес-логике, которые подходят для «сцепления» с другими. Объект бизнес-логики наследует свою функциональность от абстрактного агрегирующего класса CompositeSpecification, который содержит всего один метод IsSatisfiedBy, возвращающий булево значение. После инстанцирования объект объединяется в цепочку с другими объектами. В результате, не теряя гибкости в настройке бизнес-логики, мы можем с лёгкостью добавлять новые правила.

Примеры кода

[править | править код]
    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.