Шаблон спецификации

В компьютерном программировании шаблон спецификации — это особый шаблон проектирования программного обеспечения , посредством которого бизнес-правила можно рекомбинировать путем объединения бизнес-правил вместе с использованием логической логики . Этот шаблон часто используется в контексте предметно-ориентированного проектирования .
Шаблон спецификации описывает бизнес-правило, которое можно комбинировать с другими бизнес-правилами. В этом шаблоне единица бизнес-логики наследует свою функциональность от абстрактного агрегатного класса составной спецификации. Класс составной спецификации имеет одну функцию IsSatisfiedBy, которая возвращает логическое значение. После создания экземпляра спецификация «связывается» с другими спецификациями, что делает новые спецификации легко поддерживаемыми и в то же время легко настраиваемой бизнес-логикой. Более того, при создании экземпляра бизнес-логика может посредством вызова метода или инверсии управления изменить свое состояние, чтобы стать делегатом других классов, таких как хранилище персистентности.
В результате выполнения во время выполнения высокоуровневой бизнес-логики/предметной логики шаблон спецификации становится удобным инструментом для преобразования специальных критериев поиска пользователей в логику низкого уровня для обработки репозиториями.
Поскольку спецификация представляет собой инкапсуляцию логики в форме многократного использования, ее очень просто тщательно протестировать, и при использовании в этом контексте она также является реализацией шаблона скромного объекта.
Примеры кода
[ редактировать ]С#
[ редактировать ]public interface ISpecification
{
bool IsSatisfiedBy(object candidate);
ISpecification And(ISpecification other);
ISpecification AndNot(ISpecification other);
ISpecification Or(ISpecification other);
ISpecification OrNot(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 AndNot(ISpecification other)
{
return new AndNotSpecification(this, other);
}
public ISpecification Or(ISpecification other)
{
return new OrSpecification(this, other);
}
public ISpecification OrNot(ISpecification other)
{
return new OrNotSpecification(this, other);
}
public ISpecification Not()
{
return new NotSpecification(this);
}
}
public class AndSpecification : CompositeSpecification
{
private ISpecification leftCondition;
private ISpecification rightCondition;
public AndSpecification(ISpecification left, ISpecification right)
{
leftCondition = left;
rightCondition = right;
}
public override bool IsSatisfiedBy(object candidate)
{
return leftCondition.IsSatisfiedBy(candidate) && rightCondition.IsSatisfiedBy(candidate);
}
}
public class AndNotSpecification : CompositeSpecification
{
private ISpecification leftCondition;
private ISpecification rightCondition;
public AndNotSpecification(ISpecification left, ISpecification right)
{
leftCondition = left;
rightCondition = right;
}
public override bool IsSatisfiedBy(object candidate)
{
return leftCondition.IsSatisfiedBy(candidate) && rightCondition.IsSatisfiedBy(candidate) != true;
}
}
public class OrSpecification : CompositeSpecification
{
private ISpecification leftCondition;
private ISpecification rightCondition;
public OrSpecification(ISpecification left, ISpecification right)
{
leftCondition = left;
rightCondition = right;
}
public override bool IsSatisfiedBy(object candidate)
{
return leftCondition.IsSatisfiedBy(candidate) || rightCondition.IsSatisfiedBy(candidate);
}
}
public class OrNotSpecification : CompositeSpecification
{
private ISpecification leftCondition;
private ISpecification rightCondition;
public OrNotSpecification(ISpecification left, ISpecification right)
{
leftCondition = left;
rightCondition = right;
}
public override bool IsSatisfiedBy(object candidate)
{
return leftCondition.IsSatisfiedBy(candidate) || rightCondition.IsSatisfiedBy(candidate) != true;
}
}
public class NotSpecification : CompositeSpecification
{
private ISpecification Wrapped;
public NotSpecification(ISpecification x)
{
Wrapped = x;
}
public override bool IsSatisfiedBy(object candidate)
{
return !Wrapped.IsSatisfiedBy(candidate);
}
}
C# 6.0 с дженериками
[ редактировать ]public interface ISpecification<T>
{
bool IsSatisfiedBy(T candidate);
ISpecification<T> And(ISpecification<T> other);
ISpecification<T> AndNot(ISpecification<T> other);
ISpecification<T> Or(ISpecification<T> other);
ISpecification<T> OrNot(ISpecification<T> other);
ISpecification<T> Not();
}
public abstract class LinqSpecification<T> : CompositeSpecification<T>
{
public abstract Expression<Func<T, bool>> AsExpression();
public override bool IsSatisfiedBy(T candidate) => AsExpression().Compile()(candidate);
}
public abstract class CompositeSpecification<T> : ISpecification<T>
{
public abstract bool IsSatisfiedBy(T candidate);
public ISpecification<T> And(ISpecification<T> other) => new AndSpecification<T>(this, other);
public ISpecification<T> AndNot(ISpecification<T> other) => new AndNotSpecification<T>(this, other);
public ISpecification<T> Or(ISpecification<T> other) => new OrSpecification<T>(this, other);
public ISpecification<T> OrNot(ISpecification<T> other) => new OrNotSpecification<T>(this, other);
public ISpecification<T> Not() => new NotSpecification<T>(this);
}
public class AndSpecification<T> : CompositeSpecification<T>
{
ISpecification<T> left;
ISpecification<T> right;
public AndSpecification(ISpecification<T> left, ISpecification<T> right)
{
this.left = left;
this.right = right;
}
public override bool IsSatisfiedBy(T candidate) => left.IsSatisfiedBy(candidate) && right.IsSatisfiedBy(candidate);
}
public class AndNotSpecification<T> : CompositeSpecification<T>
{
ISpecification<T> left;
ISpecification<T> right;
public AndNotSpecification(ISpecification<T> left, ISpecification<T> right)
{
this.left = left;
this.right = right;
}
public override bool IsSatisfiedBy(T candidate) => left.IsSatisfiedBy(candidate) && !right.IsSatisfiedBy(candidate);
}
public class OrSpecification<T> : CompositeSpecification<T>
{
ISpecification<T> left;
ISpecification<T> right;
public OrSpecification(ISpecification<T> left, ISpecification<T> right)
{
this.left = left;
this.right = right;
}
public override bool IsSatisfiedBy(T candidate) => left.IsSatisfiedBy(candidate) || right.IsSatisfiedBy(candidate);
}
public class OrNotSpecification<T> : CompositeSpecification<T>
{
ISpecification<T> left;
ISpecification<T> right;
public OrNotSpecification(ISpecification<T> left, ISpecification<T> right)
{
this.left = left;
this.right = right;
}
public override bool IsSatisfiedBy(T candidate) => left.IsSatisfiedBy(candidate) || !right.IsSatisfiedBy(candidate);
}
public class NotSpecification<T> : CompositeSpecification<T>
{
ISpecification<T> other;
public NotSpecification(ISpecification<T> other) => this.other = other;
public override bool IsSatisfiedBy(T candidate) => !other.IsSatisfiedBy(candidate);
}
Питон
[ редактировать ]from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import Any
class BaseSpecification(ABC):
@abstractmethod
def is_satisfied_by(self, candidate: Any) -> bool:
raise NotImplementedError()
def __call__(self, candidate: Any) -> bool:
return self.is_satisfied_by(candidate)
def __and__(self, other: "BaseSpecification") -> "AndSpecification":
return AndSpecification(self, other)
def __or__(self, other: "BaseSpecification") -> "OrSpecification":
return OrSpecification(self, other)
def __neg__(self) -> "NotSpecification":
return NotSpecification(self)
@dataclass(frozen=True)
class AndSpecification(BaseSpecification):
first: BaseSpecification
second: BaseSpecification
def is_satisfied_by(self, candidate: Any) -> bool:
return self.first.is_satisfied_by(candidate) and self.second.is_satisfied_by(candidate)
@dataclass(frozen=True)
class OrSpecification(BaseSpecification):
first: BaseSpecification
second: BaseSpecification
def is_satisfied_by(self, candidate: Any) -> bool:
return self.first.is_satisfied_by(candidate) or self.second.is_satisfied_by(candidate)
@dataclass(frozen=True)
class NotSpecification(BaseSpecification):
subject: BaseSpecification
def is_satisfied_by(self, candidate: Any) -> bool:
return not self.subject.is_satisfied_by(candidate)
С++
[ редактировать ]template <class T>
class ISpecification
{
public:
virtual ~ISpecification() = default;
virtual bool IsSatisfiedBy(T Candidate) const = 0;
virtual ISpecification<T>* And(const ISpecification<T>& Other) const = 0;
virtual ISpecification<T>* AndNot(const ISpecification<T>& Other) const = 0;
virtual ISpecification<T>* Or(const ISpecification<T>& Other) const = 0;
virtual ISpecification<T>* OrNot(const ISpecification<T>& Other) const = 0;
virtual ISpecification<T>* Not() const = 0;
};
template <class T>
class CompositeSpecification : public ISpecification<T>
{
public:
virtual bool IsSatisfiedBy(T Candidate) const override = 0;
virtual ISpecification<T>* And(const ISpecification<T>& Other) const override;
virtual ISpecification<T>* AndNot(const ISpecification<T>& Other) const override;
virtual ISpecification<T>* Or(const ISpecification<T>& Other) const override;
virtual ISpecification<T>* OrNot(const ISpecification<T>& Other) const override;
virtual ISpecification<T>* Not() const override;
};
template <class T>
class AndSpecification final : public CompositeSpecification<T>
{
public:
const ISpecification<T>& Left;
const ISpecification<T>& Right;
AndSpecification(const ISpecification<T>& InLeft, const ISpecification<T>& InRight)
: Left(InLeft),
Right(InRight) { }
virtual bool IsSatisfiedBy(T Candidate) const override
{
return Left.IsSatisfiedBy(Candidate) && Right.IsSatisfiedBy(Candidate);
}
};
template <class T>
ISpecification<T>* CompositeSpecification<T>::And(const ISpecification<T>& Other) const
{
return new AndSpecification<T>(*this, Other);
}
template <class T>
class AndNotSpecification final : public CompositeSpecification<T>
{
public:
const ISpecification<T>& Left;
const ISpecification<T>& Right;
AndNotSpecification(const ISpecification<T>& InLeft, const ISpecification<T>& InRight)
: Left(InLeft),
Right(InRight) { }
virtual bool IsSatisfiedBy(T Candidate) const override
{
return Left.IsSatisfiedBy(Candidate) && !Right.IsSatisfiedBy(Candidate);
}
};
template <class T>
class OrSpecification final : public CompositeSpecification<T>
{
public:
const ISpecification<T>& Left;
const ISpecification<T>& Right;
OrSpecification(const ISpecification<T>& InLeft, const ISpecification<T>& InRight)
: Left(InLeft),
Right(InRight) { }
virtual bool IsSatisfiedBy(T Candidate) const override
{
return Left.IsSatisfiedBy(Candidate) || Right.IsSatisfiedBy(Candidate);
}
};
template <class T>
class OrNotSpecification final : public CompositeSpecification<T>
{
public:
const ISpecification<T>& Left;
const ISpecification<T>& Right;
OrNotSpecification(const ISpecification<T>& InLeft, const ISpecification<T>& InRight)
: Left(InLeft),
Right(InRight) { }
virtual bool IsSatisfiedBy(T Candidate) const override
{
return Left.IsSatisfiedBy(Candidate) || !Right.IsSatisfiedBy(Candidate);
}
};
template <class T>
class NotSpecification final : public CompositeSpecification<T>
{
public:
const ISpecification<T>& Other;
NotSpecification(const ISpecification<T>& InOther)
: Other(InOther) { }
virtual bool IsSatisfiedBy(T Candidate) const override
{
return !Other.IsSatisfiedBy(Candidate);
}
};
template <class T>
ISpecification<T>* CompositeSpecification<T>::AndNot(const ISpecification<T>& Other) const
{
return new AndNotSpecification<T>(*this, Other);
}
template <class T>
ISpecification<T>* CompositeSpecification<T>::Or(const ISpecification<T>& Other) const
{
return new OrSpecification<T>(*this, Other);
}
template <class T>
ISpecification<T>* CompositeSpecification<T>::OrNot(const ISpecification<T>& Other) const
{
return new OrNotSpecification<T>(*this, Other);
}
template <class T>
ISpecification<T>* CompositeSpecification<T>::Not() const
{
return new NotSpecification<T>(*this);
}
Машинопись
[ редактировать ]export interface ISpecification {
isSatisfiedBy(candidate: unknown): boolean;
and(other: ISpecification): ISpecification;
andNot(other: ISpecification): ISpecification;
or(other: ISpecification): ISpecification;
orNot(other: ISpecification): ISpecification;
not(): ISpecification;
}
export abstract class CompositeSpecification implements ISpecification {
abstract isSatisfiedBy(candidate: unknown): boolean;
and(other: ISpecification): ISpecification {
return new AndSpecification(this, other);
}
andNot(other: ISpecification): ISpecification {
return new AndNotSpecification(this, other);
}
or(other: ISpecification): ISpecification {
return new OrSpecification(this, other);
}
orNot(other: ISpecification): ISpecification {
return new OrNotSpecification(this, other);
}
not(): ISpecification {
return new NotSpecification(this);
}
}
export class AndSpecification extends CompositeSpecification {
constructor(private leftCondition: ISpecification, private rightCondition: ISpecification) {
super();
}
isSatisfiedBy(candidate: unknown): boolean {
return this.leftCondition.isSatisfiedBy(candidate) && this.rightCondition.isSatisfiedBy(candidate);
}
}
export class AndNotSpecification extends CompositeSpecification {
constructor(private leftCondition: ISpecification, private rightCondition: ISpecification) {
super();
}
isSatisfiedBy(candidate: unknown): boolean {
return this.leftCondition.isSatisfiedBy(candidate) && this.rightCondition.isSatisfiedBy(candidate) !== true;
}
}
export class OrSpecification extends CompositeSpecification {
constructor(private leftCondition: ISpecification, private rightCondition: ISpecification) {
super();
}
isSatisfiedBy(candidate: unknown): boolean {
return this.leftCondition.isSatisfiedBy(candidate) || this.rightCondition.isSatisfiedBy(candidate);
}
}
export class OrNotSpecification extends CompositeSpecification {
constructor(private leftCondition: ISpecification, private rightCondition: ISpecification) {
super();
}
isSatisfiedBy(candidate: unknown): boolean {
return this.leftCondition.isSatisfiedBy(candidate) || this.rightCondition.isSatisfiedBy(candidate) !== true;
}
}
export class NotSpecification extends CompositeSpecification {
constructor(private wrapped: ISpecification) {
super();
}
isSatisfiedBy(candidate: unknown): boolean {
return !this.wrapped.isSatisfiedBy(candidate);
}
}
Пример использования
[ редактировать ]В следующем примере счета-фактуры извлекаются и отправляются в агентство по сбору платежей, если:
- они просрочены,
- уведомления были отправлены, и
- они еще не работают в коллекторском агентстве.
Этот пример предназначен для того, чтобы показать результат того, как логика «сцеплена» вместе.
В этом примере использования предполагается ранее определенное OverdueSpecification
класс, который удовлетворяется, когда срок оплаты счета составляет 30 дней или старше, NoticeSentSpecification
класс, который удовлетворяется, когда клиенту отправлено три уведомления, и InCollectionSpecification
класс, который удовлетворяется, когда счет-фактура уже отправлен в агентство по сбору платежей. Реализация этих классов здесь не важна.
Используя эти три спецификации, мы создали новую спецификацию под названием SendToCollection
которые будут удовлетворены, если счет-фактура просрочен, когда уведомления отправлены клиенту и еще не поступили в коллекторское агентство.
var overDue = new OverDueSpecification();
var noticeSent = new NoticeSentSpecification();
var inCollection = new InCollectionSpecification();
// Example of specification pattern logic chaining
var sendToCollection = overDue.And(noticeSent).And(inCollection.Not());
var invoiceCollection = Service.GetInvoices();
foreach (var currentInvoice in invoiceCollection)
{
if (sendToCollection.IsSatisfiedBy(currentInvoice))
{
currentInvoice.SendToCollection();
}
}
Ссылки
[ редактировать ]- Эванс, Эрик (2004). Доменно-ориентированный дизайн . Аддисон-Уэсли. п. 224.
Внешние ссылки
[ редактировать ]- Технические характеристики Эрика Эванса и Мартина Фаулера
- Шаблон спецификации: учебник Мэтта Бертера
- Шаблон спецификации: введение из четырех частей с использованием VB.Net Ричарда Далтона
- Шаблон спецификации в PHP Моше Бревды
- Спецификация доктрины Happyr в PHP от Happyr
- Шаблон спецификации в Swift, Саймон Страндгаард
- Шаблон спецификации в TypeScript и JavaScript, Тьяго Дельгадо Пинто
- Шаблон спецификации во Flash Actionscript 3 от Рольфа Врейденбергера