Specification pattern: Difference between revisions

Content deleted Content added
m This NoticeSent is should to be true so match with the example above
Rdckc (talk | contribs)
m corrected compilation error
 
(42 intermediate revisions by 28 users not shown)
Line 1:
{{Short description|Software design pattern}}
[[Image:Specification_UML_v2.png|right|thumb|300px|Specification Pattern in [[Unified Modeling Language|UML]] ]]
[[File:Specification UML.png|right|thumb|300px|Specification Pattern in [[Unified Modeling Language|UML]] ]]
In computer programming, the '''specification pattern''' is a particular [[software design pattern]], whereby [[business rules]] can be recombined by chaining the business rules together using boolean logic. The pattern is frequently used in the context of [[___domain-driven design]].
In computer programming, the '''specification pattern''' is a particular [[software design pattern]], whereby [[business rules]] can be recombined by chaining the business rules together using [[boolean algebra|boolean logic]]. The pattern is frequently used in the context of [[___domain-driven design]].
 
A specification pattern outlines a business rule that is combinable with other business rules. In this pattern, a unit of business logic inherits its functionality from the abstract aggregate Composite Specification class. The Composite Specification class has one function called IsSatisfiedBy that returns a boolean value. After instantiation, the specification is "chained" with other specifications, making new specifications easily maintainable, yet highly customizable business logic. Furthermore, upon instantiation the business logic may, through method invocation or [[inversion of control]], have its state altered in order to become a delegate of other classes such as a persistence repository.
 
As a consequence of performing runtime composition of high-level business/___domain logic, the Specification pattern is a convenient tool for converting ad-hoc user search criteria into low level logic to be processed by repositories.
 
Since a specification is an encapsulation of logic in a reusable form it is very simple to thoroughly unit test, and when used in this context is also an implementation of the humble object pattern.
 
== Code examples ==
 
=== [[C Sharp (programming language)|C#]] ===
{{Further|C Sharp (programming language)}}
<syntaxhighlight lang="csharp">
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
<source lang="csharp">
{
public interface ISpecification
public abstract bool IsSatisfiedBy(object candidate);
 
public ISpecification And(ISpecification other)
{
boolreturn IsSatisfiedBynew AndSpecification(objectthis, candidateother);
ISpecification And(ISpecification other);
ISpecification AndNot(ISpecification other);
ISpecification Or(ISpecification other);
ISpecification OrNot(ISpecification other);
ISpecification Not();
}
 
public ISpecification AndNot(ISpecification other)
public abstract class CompositeSpecification : ISpecification
{
publicreturn abstractnew bool IsSatisfiedByAndNotSpecification(objectthis, candidateother);
}
 
public ISpecification AndOr(ISpecification other)
{
return new AndSpecificationOrSpecification(this, other);
}
 
public ISpecification AndNotOrNot(ISpecification other)
{
return new AndNotSpecificationOrNotSpecification(this, other);
}
 
public ISpecification OrNot(ISpecification other)
{
return new OrSpecificationNotSpecification(this, other);
}
}
 
public class AndSpecification : CompositeSpecification
public ISpecification OrNot(ISpecification other)
{
{
private ISpecification _leftCondition;
return new OrNotSpecification(this, other);
private ISpecification _rightCondition;
}
 
public AndSpecification(ISpecification publicleft, ISpecification Not(right)
{
_leftCondition = left;
return new NotSpecification(this);
}_rightCondition = right;
}
 
public override bool IsSatisfiedBy(object candidate)
public class AndSpecification : CompositeSpecification
{
return _leftCondition.IsSatisfiedBy(candidate) && _rightCondition.IsSatisfiedBy(candidate);
private ISpecification leftCondition;
}
private ISpecification rightCondition;
}
 
public class AndNotSpecification : CompositeSpecification
public AndSpecification(ISpecification left, ISpecification right)
{
{
private ISpecification _leftCondition;
leftCondition = left;
private ISpecification _rightCondition;
rightCondition = right;
}
 
public AndNotSpecification(ISpecification left, ISpecification right)
public override bool IsSatisfiedBy(object candidate)
{
_leftCondition = left;
return leftCondition.IsSatisfiedBy(candidate) && rightCondition.IsSatisfiedBy(candidate);
}_rightCondition = right;
}
 
public override bool IsSatisfiedBy(object candidate)
public class AndNotSpecification : CompositeSpecification
{
return _leftCondition.IsSatisfiedBy(candidate) && _rightCondition.IsSatisfiedBy(candidate) != true;
private ISpecification leftCondition;
}
private ISpecification rightCondition;
}
 
public class OrSpecification : CompositeSpecification
public AndNotSpecification(ISpecification left, ISpecification right)
{
{
private ISpecification _leftCondition;
leftCondition = left;
private ISpecification _rightCondition;
rightCondition = right;
}
 
public OrSpecification(ISpecification left, ISpecification right)
public override bool IsSatisfiedBy(object candidate)
{
_leftCondition = left;
return leftCondition.IsSatisfiedBy(candidate) && rightCondition.IsSatisfiedBy(candidate) != true;
}_rightCondition = right;
}
 
public override bool IsSatisfiedBy(object candidate)
public class OrSpecification : CompositeSpecification
{
return _leftCondition.IsSatisfiedBy(candidate) || _rightCondition.IsSatisfiedBy(candidate);
private ISpecification leftCondition;
}
private ISpecification rightCondition;
}
 
public class OrNotSpecification : CompositeSpecification
public OrSpecification(ISpecification left, ISpecification right)
{
{
private ISpecification _leftCondition;
leftCondition = left;
private ISpecification _rightCondition;
rightCondition = right;
}
 
public OrNotSpecification(ISpecification left, ISpecification right)
public override bool IsSatisfiedBy(object candidate)
{
_leftCondition = left;
return leftCondition.IsSatisfiedBy(candidate) || rightCondition.IsSatisfiedBy(candidate);
}_rightCondition = right;
}
 
public override bool IsSatisfiedBy(object candidate)
public class OrNotSpecification : CompositeSpecification
{
return _leftCondition.IsSatisfiedBy(candidate) || _rightCondition.IsSatisfiedBy(candidate) != true;
private ISpecification leftCondition;
}
private ISpecification rightCondition;
}
 
public class NotSpecification : CompositeSpecification
public OrNotSpecification(ISpecification left, ISpecification right)
{
{
private ISpecification _wrapped;
leftCondition = left;
rightCondition = right;
}
 
public override bool IsSatisfiedByNotSpecification(objectISpecification candidatex)
{
_wrapped = x;
return leftCondition.IsSatisfiedBy(candidate) || rightCondition.IsSatisfiedBy(candidate) != true;
}
}
 
public override bool IsSatisfiedBy(object candidate)
public class NotSpecification : CompositeSpecification
{
return !_wrapped.IsSatisfiedBy(candidate);
private ISpecification Wrapped;
}
}
</syntaxhighlight>
 
=== C# 6.0 with generics ===
public NotSpecification(ISpecification x)
{{Further|C Sharp (programming language)}}
{
<syntaxhighlight lang="csharp">
Wrapped = x;
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 override bool IsSatisfiedBy(object candidate)
{
{
public abstract Expression<Func<T, bool>> AsExpression();
return !Wrapped.IsSatisfiedBy(candidate);
public override bool IsSatisfiedBy(T candidate) => AsExpression().Compile()(candidate);
}
}
}
 
public abstract class CompositeSpecification<T> : ISpecification<T>
</source>
{
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>
=== [[C Sharp (programming language)|C# 3.0]], simplified with generics and extension methods ===
{
private ISpecification<T> _left;
private ISpecification<T> _right;
 
public AndSpecification(ISpecification<T> left, ISpecification<T> right)
<source lang="csharp">
public interface ISpecification<T>
{
bool_left IsSatisfiedBy(T= entity)left;
_right = right;
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 override bool IsSatisfiedBy(T candidate) => _left.IsSatisfiedBy(candidate) && _right.IsSatisfiedBy(candidate);
public abstract class LinqSpecification<T> : CompositeSpecification<T>
}
 
public class AndNotSpecification<T> : CompositeSpecification<T>
{
private ISpecification<T> _left;
private ISpecification<T> _right;
 
public AndNotSpecification(ISpecification<T> left, ISpecification<T> right)
{
_left = left;
public abstract Expression<Func<T, bool>> AsExpression();
_right = right;
}
 
public override bool IsSatisfiedBy(T entitycandidate) => _left.IsSatisfiedBy(candidate) && !_right.IsSatisfiedBy(candidate);
}
{
 
Func<T, bool> predicate = AsExpression().Compile();
public class OrSpecification<T> : CompositeSpecification<T>
return predicate(entity);
{
}
private ISpecification<T> _left;
private ISpecification<T> _right;
 
public OrSpecification(ISpecification<T> left, ISpecification<T> right)
{
_left = left;
_right = right;
}
 
public override bool IsSatisfiedBy(T candidate) => _left.IsSatisfiedBy(candidate) || _right.IsSatisfiedBy(candidate);
public abstract class CompositeSpecification<T> : ISpecification<T>
}
public class OrNotSpecification<T> : CompositeSpecification<T>
{
private ISpecification<T> _left;
private ISpecification<T> _right;
 
public OrNotSpecification(ISpecification<T> left, ISpecification<T> right)
{
public_left abstract= bool IsSatisfiedBy(T entity)left;
_right = right;
}
 
public override bool IsSatisfiedBy(T candidate) => _left.IsSatisfiedBy(candidate) || !_right.IsSatisfiedBy(candidate);
public ISpecification<T> And(ISpecification<T> other)
}
{
return new AndSpecification<T>(this, other);
}
 
public class NotSpecification<T> : CompositeSpecification<T>
public ISpecification<T> AndNot(ISpecification<T> other)
{
{
return new AndNotSpecificationISpecification<T>(this, other);
public NotSpecification(ISpecification<T> other) => this.other = other;
}
public override bool IsSatisfiedBy(T candidate) => !other.IsSatisfiedBy(candidate);
}
</syntaxhighlight>
 
=== Python ===
public ISpecification<T> Or(ISpecification<T> other)
{{Further|Python (programming language)}}
{
<syntaxhighlight lang="python">
return new OrSpecification<T>(this, other);
from abc import ABC, abstractmethod
}
from dataclasses import dataclass
from typing import Any
 
class BaseSpecification(ABC):
public ISpecification<T> Not()
{@abstractmethod
def is_satisfied_by(self, candidate: Any) -> bool:
return new NotSpecification<T>(this);
}raise NotImplementedError()
 
def __call__(self, candidate: Any) -> bool:
}
return self.is_satisfied_by(candidate)
 
def __and__(self, other: "BaseSpecification") -> "AndSpecification":
public class AndSpecification<T> : CompositeSpecification<T>
return AndSpecification(self, other)
{
private readonly ISpecification<T> left;
private readonly ISpecification<T> right;
 
def __or__(self, other: "BaseSpecification") -> "OrSpecification":
public AndSpecification(ISpecification<T> left, ISpecification<T> right)
return OrSpecification(self, other)
{
this.left = left;
this.right = right;
}
 
def __neg__(self) -> "NotSpecification":
public override bool IsSatisfiedBy(T candidate)
{return NotSpecification(self)
return left.IsSatisfiedBy(candidate) && right.IsSatisfiedBy(candidate);
}
}
 
@dataclass(frozen=True)
public class AndNotSpecification<T> : CompositeSpecification<T>
class AndSpecification(BaseSpecification):
{
first: BaseSpecification
private readonly ISpecification<T> left;
second: BaseSpecification
private readonly ISpecification<T> right;
 
def is_satisfied_by(self, candidate: Any) -> bool:
public AndNotSpecification(ISpecification<T> left, ISpecification<T> right)
return self.first.is_satisfied_by(candidate) and self.second.is_satisfied_by(candidate)
{
this.left = left;
this.right = right;
}
 
@dataclass(frozen=True)
public override bool IsSatisfiedBy(T candidate)
class OrSpecification(BaseSpecification):
{
first: BaseSpecification
return left.IsSatisfiedBy(candidate) && right.IsSatisfiedBy(candidate) != true;
second: BaseSpecification
}
}
 
def is_satisfied_by(self, candidate: Any) -> bool:
public class OrSpecification<T> : CompositeSpecification<T>
return self.first.is_satisfied_by(candidate) or self.second.is_satisfied_by(candidate)
{
private readonly ISpecification<T> left;
private readonly ISpecification<T> right;
 
@dataclass(frozen=True)
public OrSpecification(ISpecification<T> left, ISpecification<T> right)
class NotSpecification(BaseSpecification):
{
subject: BaseSpecification
this.left = left;
this.right = right;
}
 
def is_satisfied_by(self, candidate: Any) -> bool:
public override bool IsSatisfiedBy(T candidate)
return not self.subject.is_satisfied_by(candidate)
{
return left.IsSatisfiedBy(candidate) || right.IsSatisfiedBy(candidate);
}
}
public class OrNotSpecification<T> : CompositeSpecification<T>
{
private readonly ISpecification<T> left;
private readonly ISpecification<T> right;
 
</syntaxhighlight>
public OrNotSpecification(ISpecification<T> left, ISpecification<T> right)
{
this.left = left;
this.right = right;
}
 
=== C++ ===
public override bool IsSatisfiedBy(T candidate)
{{Further|C++}}
{
<syntaxhighlight lang="cpp">
return left.IsSatisfiedBy(candidate) || right.IsSatisfiedBy(candidate) != true;
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>
public class NotSpecification<T> : CompositeSpecification<T>
class CompositeSpecification : public ISpecification<T>
{
{
private readonly ISpecification<T> other;
public:
virtual bool IsSatisfiedBy(T Candidate) const override = 0;
 
virtual ISpecification<T>* public NotSpecificationAnd(const ISpecification<T>& otherOther) const override;
virtual ISpecification<T>* AndNot(const ISpecification<T>& Other) const override;
{
virtual ISpecification<T>* Or(const ISpecification<T>& Other) const override;
this.other = other;
virtual ISpecification<T>* OrNot(const ISpecification<T>& Other) const override;
}
virtual ISpecification<T>* Not() const override;
};
 
template <class T>
public override bool IsSatisfiedBy(T candidate)
class AndSpecification final : public CompositeSpecification<T>
{
{
return !other.IsSatisfiedBy(candidate);
public:
}
const ISpecification<T>& Left;
}
const ISpecification<T>& Right;
</source>
 
AndSpecification(const ISpecification<T>& InLeft, const ISpecification<T>& InRight)
==Example of use==
: Left(InLeft),
Right(InRight) { }
 
virtual bool IsSatisfiedBy(T Candidate) const override
In the following example, we are retrieving invoices and sending them to a collection agency if
{
return Left.IsSatisfiedBy(Candidate) && Right.IsSatisfiedBy(Candidate);
}
};
 
template <class T>
# they are overdue,
ISpecification<T>* CompositeSpecification<T>::And(const ISpecification<T>& Other) const
# notices have been sent, and
{
# they are not already with the collection agency.
return new AndSpecification<T>(*this, Other);
}
 
template <class T>
This example is meant to show the end result of how the logic is 'chained' together.
class AndNotSpecification final : public CompositeSpecification<T>
{
public:
const ISpecification<T>& Left;
const ISpecification<T>& Right;
 
AndNotSpecification(const ISpecification<T>& InLeft, const ISpecification<T>& InRight)
This usage example assumes a previously defined OverdueSpecification class that is satisfied when an invoice's due date is 30 days or older, a NoticeSentSpecification class that is satisfied when three notices have been sent to the customer, and an InCollectionSpecification class that is satisfied when an invoice has already been sent to the collection agency. The implementation of these classes isn't important here.
: Left(InLeft),
Right(InRight) { }
 
virtual bool IsSatisfiedBy(T Candidate) const override
Using these three specifications, we created a new specification called SendToCollection which will be satisfied when an invoice is overdue, when notices have been sent to the customer, and are not already with the collection agency.
{
return Left.IsSatisfiedBy(Candidate) && !Right.IsSatisfiedBy(Candidate);
}
};
 
template <class T>
<source lang="csharp">
class OrSpecification final : public CompositeSpecification<T>
OverDueSpecification OverDue = new OverDueSpecification();
{
NoticeSentSpecification NoticeSent = new NoticeSentSpecification();
public:
InCollectionSpecification InCollection = new InCollectionSpecification();
const ISpecification<T>& Left;
const ISpecification<T>& Right;
 
OrSpecification(const ISpecification<T>& InLeft, const ISpecification<T>& InRight)
// example of specification pattern logic chaining
: Left(InLeft),
ISpecification<Invoice> SendToCollection = OverDue.And(NoticeSent).And(InCollection.Not());
Right(InRight) { }
 
virtual bool IsSatisfiedBy(T Candidate) const override
InvoiceCollection = Service.GetInvoices();
{
return Left.IsSatisfiedBy(Candidate) || Right.IsSatisfiedBy(Candidate);
}
};
 
template <class T>
foreach (Invoice currentInvoice in InvoiceCollection) {
class OrNotSpecification final : public CompositeSpecification<T>
if (SendToCollection.IsSatisfiedBy(currentInvoice)) {
{
currentInvoice.SendToCollection();
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);
}
</source>
 
template <class T>
Contrast without the Specification Pattern:
ISpecification<T>* CompositeSpecification<T>::Or(const ISpecification<T>& Other) const
{
return new OrSpecification<T>(*this, Other);
}
 
template <class T>
<source lang="csharp">
ISpecification<T>* CompositeSpecification<T>::OrNot(const ISpecification<T>& Other) const
InvoiceCollection = Service.GetInvoices();
{
foreach (Invoice currentInvoice in InvoiceCollection) {
return new OrNotSpecification<T>(*this, Other);
currentInvoice.SendToCollectionIfNecessary();
}
 
template <class T>
//.. in the Invoice partial class:
ISpecification<T>* CompositeSpecification<T>::Not() const
{
return new NotSpecification<T>(*this);
}
 
</syntaxhighlight>
public bool ShouldSendToCollection { get { return currentInvoice.OverDue && currentInvoice.NoticeSent && currentInvoice.InCollection == false; }}
 
=== TypeScript ===
public void SendToCollectionIfNecessary()
{{Further|TypeScript}}
<syntaxhighlight lang="typescript">
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);
}
}
</syntaxhighlight>
 
==Example of use==
 
In the next example, invoices are retrieved and sent to a collection agency if:
 
# they are overdue,
# notices have been sent, and
# they are not already with the collection agency.
 
This example is meant to show the result of how the logic is 'chained' together.
 
This usage example assumes a previously defined <code>OverdueSpecification</code> class that is satisfied when an invoice's due date is 30 days or older, a <code>NoticeSentSpecification</code> class that is satisfied when three notices have been sent to the customer, and an <code>InCollectionSpecification</code> class that is satisfied when an invoice has already been sent to the collection agency. The implementation of these classes isn't important here.
 
Using these three specifications, we created a new specification called <code>SendToCollection</code> which will be satisfied when an invoice is overdue, when notices have been sent to the customer, and are not already with the collection agency.
 
<syntaxhighlight lang="csharp">
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 invoices = InvoiceService.GetInvoices();
 
foreach (var invoice in invoices)
{
if (sendToCollection.IsSatisfiedBy(invoice))
//Guard condition - with each of those new properties
{
if (!ShouldSendToCollection) return;
this invoice.SendToCollection();
}
}
</syntaxhighlight>
</source>
 
==References==
Line 328 ⟶ 553:
* [https://github.com/Happyr/Doctrine-Specification Happyr Doctrine Specification in PHP] by Happyr
* [https://github.com/neoneye/SpecificationPattern The Specification Pattern in Swift] by Simon Strandgaard
* [https://github.com/thiagodp/spec-pattern The Specification Pattern in TypeScript and JavaScript] by Thiago Delgado Pinto
* [https://web.archive.org/20110724151447/http://www.dpdk.nl/opensource/specification-pattern-for-selection-on-lists specification pattern in flash actionscript 3] by Rolf Vreijdenberger
* [https://web.archive.org/web/20110724151447/http://www.dpdk.nl/opensource/specification-pattern-for-selection-on-lists specification pattern in flash actionscript 3] by Rolf Vreijdenberger
 
{{Design Patterns Patternspatterns}}
 
[[Category:Architectural pattern (computer science)]]
[[Category:Software design patterns]]
[[Category:Programming language comparisons]]
<!-- Hidden categories below -->
[[Category:Articles with example C Sharp code]]
[[Category:Articles with example C++ code]]
[[Category:Articles with example JavaScript code]]
[[Category:Articles with example Python (programming language) code]]