Jump to content

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

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

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

Шаблон спецификации описывает бизнес-правило, которое можно комбинировать с другими бизнес-правилами. В этом шаблоне единица бизнес-логики наследует свою функциональность от абстрактного агрегатного класса составной спецификации. Класс составной спецификации имеет одну функцию 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);
  }
}

Пример использования

[ редактировать ]

В следующем примере счета-фактуры извлекаются и отправляются в агентство по сбору платежей, если:

  1. они просрочены,
  2. уведомления были отправлены, и
  3. они еще не работают в коллекторском агентстве.

Этот пример предназначен для того, чтобы показать результат того, как логика «сцеплена» вместе.

В этом примере использования предполагается ранее определенное 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.
[ редактировать ]
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: c1bd0cd827c2e499794dea3f85b6d17d__1722542400
URL1:https://arc.ask3.ru/arc/aa/c1/7d/c1bd0cd827c2e499794dea3f85b6d17d.html
Заголовок, (Title) документа по адресу, URL1:
Specification pattern - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)