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

Этот шаблон выделяет такие спецификации (правила) в бизнес-логике, которые подходят для «сцепления» с другими. Объект бизнес-логики наследует свою функциональность от абстрактного агрегирующего класса 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

Категория:Шаблоны проектирования Категория:Статьи с примерами кода C Sharp