Specification pattern: Difference between revisions

Content deleted Content added
m remove unecessary comment
Rdckc (talk | contribs)
m corrected compilation error
 
(15 intermediate revisions by 11 users not shown)
Line 1:
{{shortShort description|Software design pattern}}
[[ImageFile: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 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
{
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 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 classISpecification AndSpecificationOr(ISpecification : CompositeSpecificationother)
{
return new OrSpecification(this, other);
private ISpecification leftCondition;
}
private ISpecification rightCondition;
 
public AndSpecification(ISpecification left, OrNot(ISpecification rightother)
{
return new OrNotSpecification(this, other);
leftCondition = left;
rightCondition = right;
}
 
public override bool IsSatisfiedBy(object candidate)
{
return leftCondition.IsSatisfiedBy(candidate) && rightCondition.IsSatisfiedBy(candidate);
}
}
 
public ISpecification Not()
public class AndNotSpecification : CompositeSpecification
{
return new NotSpecification(this);
private ISpecification leftCondition;
}
private ISpecification rightCondition;
}
 
public class AndSpecification : CompositeSpecification
public AndNotSpecification(ISpecification left, ISpecification right)
{
{
private ISpecification _leftCondition;
leftCondition = left;
private ISpecification _rightCondition;
rightCondition = right;
}
 
public AndSpecification(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 AndNotSpecification : CompositeSpecification
public OrSpecification(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 OrNotSpecification : CompositeSpecification
{
return _leftCondition.IsSatisfiedBy(candidate) && _rightCondition.IsSatisfiedBy(candidate) != true;
private ISpecification leftCondition;
}
private ISpecification rightCondition;
}
 
public class OrSpecification : CompositeSpecification
public OrNotSpecification(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 NotSpecification : CompositeSpecification
{
return _leftCondition.IsSatisfiedBy(candidate) || _rightCondition.IsSatisfiedBy(candidate);
private ISpecification Wrapped;
 
public NotSpecification(ISpecification x)
{
Wrapped = x;
}
 
public override bool IsSatisfiedBy(object candidate)
{
return !Wrapped.IsSatisfiedBy(candidate);
}
}
}
 
public class OrNotSpecification : CompositeSpecification
</syntaxhighlight>
{
private ISpecification _leftCondition;
private ISpecification _rightCondition;
 
public OrNotSpecification(ISpecification left, ISpecification right)
=== [[C Sharp (programming language)|C# 6.0]] with generics ===
 
<syntaxhighlight lang="csharp">
public interface ISpecification<T>
{
bool_leftCondition IsSatisfiedBy(T= candidate)left;
_rightCondition = 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(object candidate)
public abstract class LinqSpecification<T> : CompositeSpecification<T>
{
return _leftCondition.IsSatisfiedBy(candidate) || _rightCondition.IsSatisfiedBy(candidate) != true;
public abstract Expression<Func<T, bool>> AsExpression();
public override bool IsSatisfiedBy(T candidate) => AsExpression().Compile()(candidate);
}
}
 
public class NotSpecification : CompositeSpecification
{
private ISpecification _wrapped;
 
public NotSpecification(ISpecification x)
public abstract class CompositeSpecification<T> : ISpecification<T>
{
_wrapped = x;
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 override bool IsSatisfiedBy(object candidate)
public class AndSpecification<T> : CompositeSpecification<T>
{
return !_wrapped.IsSatisfiedBy(candidate);
ISpecification<T> left;
}
ISpecification<T> right;
}
</syntaxhighlight>
 
=== C# 6.0 with generics ===
public AndSpecification(ISpecification<T> left, ISpecification<T> right)
{{Further|C Sharp (programming language)}}
{
<syntaxhighlight lang="csharp">
this.left = left;
public interface ISpecification<T>
this.right = right;
{
}
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(T candidate) => left.IsSatisfiedBy(candidate) && right.IsSatisfiedBy(candidate);
{
}
public abstract Expression<Func<T, bool>> AsExpression();
public override bool IsSatisfiedBy(T candidate) => AsExpression().Compile()(candidate);
}
 
public publicabstract class AndNotSpecificationCompositeSpecification<T> : CompositeSpecificationISpecification<T>
{
{
public abstract bool IsSatisfiedBy(T candidate);
ISpecification<T> left;
public ISpecification<T> And(ISpecification<T> other) => new AndSpecification<T>(this, other);
ISpecification<T> right;
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>
public AndNotSpecification(ISpecification<T> left, ISpecification<T> right)
{
{
private ISpecification<T> _left;
this.left = left;
private ISpecification<T> _right;
this.right = right;
}
 
public AndSpecification(ISpecification<T> left, ISpecification<T> right)
public override bool IsSatisfiedBy(T candidate) => left.IsSatisfiedBy(candidate) && !right.IsSatisfiedBy(candidate);
{
_left = left;
_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 class AndNotSpecification<T> : CompositeSpecification<T>
public OrSpecification(ISpecification<T> left, ISpecification<T> right)
{
{
private ISpecification<T> _left;
this.left = left;
private ISpecification<T> _right;
this.right = right;
}
 
public AndNotSpecification(ISpecification<T> left, ISpecification<T> right)
public override bool IsSatisfiedBy(T candidate) => left.IsSatisfiedBy(candidate) || right.IsSatisfiedBy(candidate);
}
public class OrNotSpecification<T> : CompositeSpecification<T>
{
ISpecification<T>_left = left;
ISpecification<T>_right = right;
}
 
public override bool IsSatisfiedBy(T candidate) => _left.IsSatisfiedBy(candidate) && !_right.IsSatisfiedBy(candidate);
public OrNotSpecification(ISpecification<T> left, ISpecification<T> right)
}
{
this.left = left;
this.right = right;
}
 
public class OrSpecification<T> : CompositeSpecification<T>
public override bool IsSatisfiedBy(T candidate) => left.IsSatisfiedBy(candidate) || !right.IsSatisfiedBy(candidate);
{
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 class NotSpecification<T> : CompositeSpecification<T>
}
public class OrNotSpecification<T> : CompositeSpecification<T>
{
private ISpecification<T> _left;
private ISpecification<T> _right;
 
public OrNotSpecification(ISpecification<T> left, ISpecification<T> right)
{
ISpecification<T>_left other= left;
_right = right;
public NotSpecification(ISpecification<T> other) => this.other = other;
public override bool IsSatisfiedBy(T candidate) => !other.IsSatisfiedBy(candidate);
}
 
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);
}
</syntaxhighlight>
 
=== [[Python (programming language)|Python]] ===
{{Further|Python (programming language)}}
<syntaxhighlight lang="python">
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:
Line 241 ⟶ 243:
 
def __call__(self, candidate: Any) -> bool:
return self.is_satisfied_by(candidate)
 
def __and__(self, other: "BaseSpecification") -> "AndSpecification":
Line 277 ⟶ 279:
</syntaxhighlight>
 
=== [[C++ (programming language)|C++]] ===
{{Further|C++}}
<syntaxhighlight lang="cpp">
template <class T>
Line 418 ⟶ 421:
}
 
</syntaxhighlight>
 
=== TypeScript ===
{{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 followingnext example, weinvoices are retrieving invoicesretrieved and sending themsent to a collection agency if:
 
# they are overdue,
Line 428 ⟶ 518:
# they are not already with the collection agency.
 
This example is meant to show the end 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 OverDueoverdue = new OverDueSpecificationOverdueSpecification();
var NoticeSentnoticeSent = new NoticeSentSpecification();
var InCollectioninCollection = new InCollectionSpecification();
 
// exampleExample of specification pattern logic chaining
var SendToCollectionsendToCollection = OverDueoverdue.And(NoticeSentnoticeSent).And(InCollectioninCollection.Not());
 
var InvoiceCollectioninvoices = ServiceInvoiceService.GetInvoices();
 
foreach (var currentInvoiceinvoice in InvoiceCollectioninvoices) {
{
if (SendToCollection.IsSatisfiedBy(currentInvoice)) {
if (sendToCollection.IsSatisfiedBy(invoice))
currentInvoice.SendToCollection();
{
invoice.SendToCollection();
}
}
Line 464 ⟶ 556:
* [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:Articles with example C Sharp code]]
[[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]]