
En programación informática, el patrón de especificación es un patrón de diseño de software particular que permite recombinar reglas de negocio encadenándolas mediante lógica booleana . Este patrón se utiliza frecuentemente en el contexto del diseño orientado al dominio .
Un patrón de especificación define una regla de negocio que se puede combinar con otras reglas de negocio. En este patrón, una unidad de lógica de negocio hereda su funcionalidad de la clase abstracta agregada Composite Specification. La clase Composite Specification tiene una función llamada IsSatisfiedBy que devuelve un valor booleano. Tras su instanciación, la especificación se "encadena" con otras especificaciones, lo que facilita el mantenimiento de nuevas especificaciones y, a la vez, permite una lógica de negocio altamente personalizable. Además, al instanciarse, la lógica de negocio puede, mediante la invocación de métodos o la inversión de control , modificar su estado para convertirse en delegada de otras clases, como un repositorio de persistencia.
Como consecuencia de realizar la composición en tiempo de ejecución de la lógica empresarial/de dominio de alto nivel, el patrón Specification es una herramienta conveniente para convertir los criterios de búsqueda de usuario ad hoc en lógica de bajo nivel que será procesada por los repositorios.
Dado que una especificación es una encapsulación de la lógica en un formato reutilizable, es muy sencillo realizar pruebas unitarias exhaustivas y, cuando se utiliza en este contexto, también constituye una implementación del sencillo patrón de objeto.
Ejemplos de código
DO#
interfaz pública ISpecification { bool IsSatisfiedBy ( objeto candidato ); ISpecification And ( ISpecification otro ); ISpecification AndNot ( ISpecification otro ); ISpecification Or ( ISpecification otro ); ISpecification OrNot ( ISpecification otro ); 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 ); } }clase pública 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 ; } }clase pública NotSpecification : CompositeSpecification { privado ISpecification _wrapped ;public NotSpecification ( ISpecification x ) { _wrapped = x ; }public override bool IsSatisfiedBy ( object candidate ) { return ! _wrapped . IsSatisfiedBy ( candidate ); } }C# 6.0 con genéricos
interfaz pública 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 > { private ISpecification < T > _left ; private ISpecification < T > _right ;public AndSpecification ( ISpecification < T > left , ISpecification < T > right ) { _left = left ; _right = right ; }public override bool IsSatisfiedBy ( T candidate ) => _left . IsSatisfiedBy ( candidate ) && _right . IsSatisfiedBy ( candidate ); }public class AndNotSpecification < T > : CompositeSpecification < T > { private ISpecification < T > _left ; private ISpecification < T > _right ;public AndNotSpecification ( ISpecification < T > left , ISpecification < T > right ) { _left = left ; _right = right ; }public override bool IsSatisfiedBy ( T candidate ) => _left . IsSatisfiedBy ( candidate ) && ! _right . IsSatisfiedBy ( candidate ); }public class OrSpecification < T > : CompositeSpecification < T > { 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 OrNotSpecification < T > : CompositeSpecification < T > { private ISpecification < T > _left ; private ISpecification < T > _right ;public OrNotSpecification ( 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 > { ISpecification < T > other ; public NotSpecification ( ISpecification < T > other ) => this . other = other ; public override bool IsSatisfiedBy ( T candidate ) => ! other . IsSatisfiedBy ( candidate ); }Pitón
from abc import ABC , abstractmethod from dataclasses import dataclass from typing import Anyclase 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 : BaseSpecificationdef 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 : BaseSpecificationdef 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 : BaseSpecificationdef is_satisfied_by ( self , candidate : Any ) -> bool : return not self . subject . is_satisfied_by ( candidate )C++
plantilla < clase T > clase ISpecification { público : virtual ~ ISpecification () = predeterminado ; 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 ; };plantilla < clase T > clase 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 ; };plantilla < clase T > clase 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 ); } };plantilla < clase T > ISpecification < T >* CompositeSpecification < T >:: And ( const ISpecification < T >& Other ) const { return new AndSpecification < T > ( * this , Other ); }plantilla < clase T > clase 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 ); } };plantilla < clase T > clase 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 ); } };plantilla < clase T > clase 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 ); } };plantilla < clase T > clase 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 ); } };plantilla < clase T > ISpecification < T >* CompositeSpecification < T >:: AndNot ( const ISpecification < T >& Other ) const { return new AndNotSpecification < T > ( * this , Other ); }plantilla < clase T > ISpecification < T >* CompositeSpecification < T >:: Or ( const ISpecification < T >& Other ) const { return new OrSpecification < T > ( * this , Other ); }plantilla < clase T > ISpecification < T >* CompositeSpecification < T >:: OrNot ( const ISpecification < T >& Other ) const { return new OrNotSpecification < T > ( * this , Other ); }plantilla < clase T > ISpecification < T >* CompositeSpecification < T >:: Not () const { return new NotSpecification < T > ( * this ); }Mecanografiado
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 ;y ( otro : ISpecification ) : ISpecification { return new AndSpecification ( this , other ); }andNot ( other : ISpecification ) : ISpecification { return new AndNotSpecification ( this , other ); }o ( otro : ISpecification ) : ISpecification { return new OrSpecification ( este , otro ); }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 ( candidato : desconocido ) : booleano { devuelve esto . Condición izquierda . isSatisfiedBy ( candidato ) && esto . Condición correcta . está satisfecho por ( candidato ); } }export class AndNotSpecification extends CompositeSpecification { constructor ( private leftCondition : ISpecification , private rightCondition : ISpecification ) { super (); }isSatisfiedBy ( candidato : desconocido ) : booleano { devuelve esto . Condición izquierda . isSatisfiedBy ( candidato ) && esto . Condición correcta . isSatisfiedBy ( candidato ) !== verdadero ; } }export class OrSpecification extends CompositeSpecification { constructor ( private leftCondition : ISpecification , private rightCondition : ISpecification ) { super (); }isSatisfiedBy ( candidato : desconocido ) : booleano { devuelve esto . Condición izquierda . isSatisfiedBy ( candidato ) || este . Condición correcta . está satisfecho por ( candidato ); } }export class OrNotSpecification extends CompositeSpecification { constructor ( private leftCondition : ISpecification , private rightCondition : ISpecification ) { super (); }isSatisfiedBy ( candidato : desconocido ) : booleano { devuelve esto . Condición izquierda . isSatisfiedBy ( candidato ) || este . Condición correcta . isSatisfiedBy ( candidato ) !== verdadero ; } }export class NotSpecification extends CompositeSpecification { constructor ( private wrapped : ISpecification ) { super (); }isSatisfiedBy ( candidato : desconocido ) : booleano { return ! this . wrapped . isSatisfiedBy ( candidato ); } }Ejemplo de uso
En el siguiente ejemplo, las facturas se recuperan y se envían a una agencia de cobro si:
- Están vencidos,
- Se han enviado avisos y
- No están ya en manos de la agencia de cobranza.
Este ejemplo pretende mostrar el resultado de cómo se "encadena" la lógica.
Este ejemplo de uso presupone una OverdueSpecificationclase previamente definida que se cumple cuando la fecha de vencimiento de una factura es de 30 días o más, otra NoticeSentSpecificationclase que se cumple cuando se han enviado tres notificaciones al cliente y una InCollectionSpecificationtercera clase que se cumple cuando la factura ya se ha enviado a la agencia de cobro. La implementación de estas clases no es relevante en este caso.
Utilizando estas tres especificaciones, creamos una nueva especificación SendToCollectionque se cumplirá cuando una factura esté vencida, cuando se hayan enviado avisos al cliente y no esté ya en manos de la agencia de cobranza.
var overdue = new OverdueSpecification (); var noticeSent = new NoticeSentSpecification (); var inCollection = new InCollectionSpecification ();// Ejemplo de encadenamiento de lógica de patrón de especificación var sendToCollection = overdue . And ( noticeSent ). And ( inCollection . Not ());var facturas = InvoiceService.GetInvoices ( ) ;foreach ( var invoice in invoices ) { if ( sendToCollection . IsSatisfiedBy ( invoice )) { invoice . SendToCollection (); } }Referencias
- Evans, Eric (2004). Diseño orientado al dominio . Addison-Wesley. pág. 224.
Enlaces externos
- Especificaciones de Eric Evans y Martin Fowler
- El patrón de especificación: una introducción por Matt Berther
- El patrón de especificación: una introducción en cuatro partes usando VB.Net por Richard Dalton
- El patrón de especificación en PHP por Moshe Brevda
- Especificación de Happyr Doctrine en PHP por Happyr
- El patrón de especificación en Swift por Simon Strandgaard
- El patrón de especificación en TypeScript y JavaScript por Thiago Delgado Pinto
- Patrón de especificación en Flash ActionScript 3 por Rolf Vreijdenberger
- Patrón arquitectónico (informática)
- patrones de diseño de software
- Comparación de lenguajes de programación