
In computer programming, the specification pattern is a particular
software design pattern
In software engineering, a software design pattern or design pattern is a general, reusable solution to a commonly occurring problem in many contexts in software design. A design pattern is not a rigid structure to be transplanted directly into s ...
, whereby
business rules A business rule defines or constrains some aspect of a business. It may be expressed to specify an action to be taken when certain conditions are true or may be phrased so it can only resolve to either true or false. Business rules are intended to a ...
can be recombined by chaining the business rules together using
boolean logic
In mathematics and mathematical logic, Boolean algebra is a branch of algebra. It differs from elementary algebra in two ways. First, the values of the variable (mathematics), variables are the truth values ''true'' and ''false'', usually denot ...
. The pattern is frequently used in the context of
domain-driven design
Domain-driven design (DDD) is a major software design approach, focusing on modeling software to match a domain according to input from that domain's experts. DDD is against the idea of having a single unified model; instead it divides a large s ...
.
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
In software engineering, inversion of control (IoC) is a design principle in which custom-written portions of a computer program receive the flow of control from an external source (e.g. a framework). The term "inversion" is historical: a softw ...
, 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#
public interface ISpecification
public abstract class CompositeSpecification : ISpecification
public class AndSpecification : CompositeSpecification
public class AndNotSpecification : CompositeSpecification
public class OrSpecification : CompositeSpecification
public class OrNotSpecification : CompositeSpecification
public class NotSpecification : CompositeSpecification
C# 6.0 with generics
public interface ISpecification
public abstract class LinqSpecification : CompositeSpecification
public abstract class CompositeSpecification : ISpecification
public class AndSpecification : CompositeSpecification
public class AndNotSpecification : CompositeSpecification
public class OrSpecification : CompositeSpecification
public class OrNotSpecification : CompositeSpecification
public class NotSpecification : CompositeSpecification
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:
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)
C++
template
class ISpecification
;
template
class CompositeSpecification : public ISpecification
;
template
class AndSpecification final : public CompositeSpecification
;
template
ISpecification* CompositeSpecification::And(const ISpecification& Other) const
template
class AndNotSpecification final : public CompositeSpecification
;
template
class OrSpecification final : public CompositeSpecification
;
template
class OrNotSpecification final : public CompositeSpecification
;
template
class NotSpecification final : public CompositeSpecification
;
template
ISpecification* CompositeSpecification::AndNot(const ISpecification& Other) const
template
ISpecification* CompositeSpecification::Or(const ISpecification& Other) const
template
ISpecification* CompositeSpecification::OrNot(const ISpecification& Other) const
template
ISpecification* CompositeSpecification::Not() const
TypeScript
export interface ISpecification
export abstract class CompositeSpecification implements ISpecification
export class AndSpecification extends CompositeSpecification
export class AndNotSpecification extends CompositeSpecification
export class OrSpecification extends CompositeSpecification
export class OrNotSpecification extends CompositeSpecification
export class NotSpecification extends CompositeSpecification
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
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.
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.
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)
References
*
External links
Specificationsby Eric Evans and Martin Fowler
The Specification Pattern: A Primerby Matt Berther
The Specification Pattern: A Four Part Introduction using VB.Net by Richard Dalton
The Specification Pattern in PHPby Moshe Brevda
Happyr Doctrine Specification in PHPby Happyr
The Specification Pattern in Swiftby Simon Strandgaard
The Specification Pattern in TypeScript and JavaScriptby Thiago Delgado Pinto
specification pattern in flash actionscript 3by Rolf Vreijdenberger
{{Design patterns
Architectural pattern (computer science)
Software design patterns
Programming language comparisons
Articles with example C Sharp code
Articles with example C++ code
Articles with example JavaScript code
Articles with example Python (programming language) code