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

Материал из Википедии — свободной энциклопедии
Перейти к: навигация, поиск
Шаблон проектирования «Спецификация» в виде UML диаграммы

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

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

Ссылки[править | править вики-текст]