En la programación de computadoras, el patrón de especificación es un patrón de diseño de software particular , mediante el cual las reglas comerciales se pueden recombinar encadenando las reglas comerciales mediante la lógica booleana. El patrón se utiliza con frecuencia en el contexto del diseño impulsado por dominios .
Un patrón de especificación describe una regla comercial que se puede combinar con otras reglas comerciales. En este patrón, una unidad de lógica empresarial hereda su funcionalidad de la clase de especificación compuesta agregada abstracta. La clase Composite Specification tiene una función llamada IsSatisfiedBy que devuelve un valor booleano. Después de la instanciación, la especificación se "encadena" con otras especificaciones, lo que hace que las nuevas especificaciones sean fáciles de mantener, pero con una lógica empresarial altamente personalizable. Además, tras la creación de instancias, la lógica empresarial puede, mediante la invocación de un método o la inversión del control , tener su estado alterado para convertirse en un delegado 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 de especificación es una herramienta conveniente para convertir los criterios de búsqueda de usuarios ad-hoc en una lógica de bajo nivel para ser procesada por los repositorios.
Ejemplos de código
C#
pública interfaz ISpecification { bool IsSatisfiedBy ( objeto candidato ); ISpecification Y ( ISpecification otro ); ISpecification AndNot ( ISpecification otro ); ISpecification O ( ISpecification other ); ISpecification OrNot ( ISpecification other ); ISpecification Not (); } clase pública abstracta CompositeSpecification : ISpecification { public abstract bool IsSatisfiedBy ( objeto candidato ); pública ISpecification Y ( ISpecification otra ) { vuelven nueva AndSpecification ( este , otra ); } pública ISpecification ANDNOT ( ISpecification otra ) { devolver nueva AndNotSpecification ( este , otra ); } pública ISpecification O ( ISpecification otra ) { devolver nueva OrSpecification ( este , otra ); } pública ISpecification ornot ( ISpecification otra ) { devolver nueva OrNotSpecification ( este , otra ); } public ISpecification Not () { devolver nueva NotSpecification ( esto ); } } público de clase AndSpecification : CompositeSpecification { privado ISpecification leftCondition ; privado ISpecification rightCondition ; AndSpecification pública ( ISpecification izquierda , ISpecification derecha ) { leftCondition = left ; rightCondition = correcto ; } public override bool IsSatisfiedBy ( candidato de objeto ) { return leftCondition . IsSatisfiedBy ( candidato ) && rightCondition . IsSatisfiedBy ( candidato ); } } público de clase AndNotSpecification : CompositeSpecification { privado ISpecification leftCondition ; privado ISpecification rightCondition ; pública AndNotSpecification ( ISpecification izquierda , ISpecification derecha ) { leftCondition = izquierda ; rightCondition = correcto ; } public override bool IsSatisfiedBy ( candidato de objeto ) { return leftCondition . IsSatisfiedBy ( candidato ) && rightCondition . IsSatisfiedBy ( candidato ) ! = Verdadero ; } } clase pública OrSpecification : CompositeSpecification { private ISpecification leftCondition ; privado ISpecification rightCondition ; pública OrSpecification ( ISpecification izquierda , ISpecification derecha ) { leftCondition = izquierda ; rightCondition = correcto ; } public override bool IsSatisfiedBy ( candidato de objeto ) { return leftCondition . IsSatisfiedBy ( candidato ) || rightCondition . IsSatisfiedBy ( candidato ); } } clase pública OrNotSpecification : CompositeSpecification { ISpecification privada leftCondition ; privado ISpecification rightCondition ; public OrNotSpecification ( ISpecification izquierda , ISpecification derecha ) { leftCondition = left ; rightCondition = correcto ; } public override bool IsSatisfiedBy ( candidato de objeto ) { return leftCondition . IsSatisfiedBy ( candidato ) || rightCondition . IsSatisfiedBy ( candidato ) ! = Verdadero ; } } público de clase NotSpecification : CompositeSpecification { privado ISpecification envuelto ; público NotSpecification ( ISpecification x ) { envuelta = x ; } public override bool IsSatisfiedBy ( candidato de objeto ) { return ! Envuelto . IsSatisfiedBy ( candidato ); } }
C # 6.0 con genéricos
pública interfaz ISpecification < T > { bool IsSatisfiedBy ( T candidato ); ISpecification < T > And ( ISpecification < T > other ); ISpecification < T > AndNot ( ISpecification < T > other ); ISpecification < T > O ( ISpecification < T > other ); ISpecification < T > OrNot ( ISpecification < T > other ); ISpecificación < T > Not (); } clase abstracta pública LinqSpecification < T > : CompositeSpecification < T > { Expresión abstracta pública < Func < T , bool >> AsExpression (); public override bool IsSatisfiedBy ( candidato T ) => AsExpression (). Compile () ( candidato ); } public abstract class CompositeSpecification < T > : ISpecification < T > { public abstract bool IsSatisfiedBy ( candidato T ); public ISpecification < T > And ( ISpecification < T > other ) => new AndSpecification < T > ( esto , otro ); public ISpecification < T > AndNot ( ISpecification < T > other ) => new AndNotSpecification < T > ( esto , otro ); 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 > ( esto ); } clase pública AndSpecification < T > : CompositeSpecification < T > { ISpecification < T > izquierda ; ISpecification < T > derecha ; public AndSpecification ( ISpecification < T > izquierda , ISpecification < T > derecha ) { this . izquierda = izquierda ; esto . derecha = derecha ; } public override bool IsSatisfiedBy ( candidato T ) => izquierda . IsSatisfiedBy ( candidato ) && correcto . IsSatisfiedBy ( candidato ); } clase pública AndNotSpecification < T > : CompositeSpecification < T > { ISpecification < T > left ; ISpecification < T > derecha ; public AndNotSpecification ( ISpecification < T > izquierda , ISpecification < T > derecha ) { this . izquierda = izquierda ; esto . derecha = derecha ; } public override bool IsSatisfiedBy ( candidato T ) => izquierda . IsSatisfiedBy ( candidato ) && ! bien . IsSatisfiedBy ( candidato ); } clase pública OrSpecification < T > : CompositeSpecification < T > { ISpecification < T > izquierda ; ISpecification < T > derecha ; public OrSpecification ( ISpecification < T > izquierda , ISpecification < T > derecha ) { this . izquierda = izquierda ; esto . derecha = derecha ; } public override bool IsSatisfiedBy ( candidato T ) => izquierda . IsSatisfiedBy ( candidato ) || bien . IsSatisfiedBy ( candidato ); } public class OrNotSpecification < T > : CompositeSpecification < T > { ISpecification < T > left ; ISpecification < T > derecha ; public OrNotSpecification ( ISpecification < T > izquierda , ISpecification < T > derecha ) { this . izquierda = izquierda ; esto . derecha = derecha ; } public override bool IsSatisfiedBy ( candidato T ) => izquierda . IsSatisfiedBy ( candidato ) || ! bien . IsSatisfiedBy ( candidato ); } clase pública NotSpecification < T > : CompositeSpecification < T > { ISpecification < T > otro ; public NotSpecification ( ISpecification < T > other ) => this . otro = otro ; public override bool IsSatisfiedBy ( candidato T ) => ! otros . IsSatisfiedBy ( candidato ); }
Pitón
desde abc importar método abstracto desde clases de datos importar clase de datos desde escribir importar Cualquieraclass BaseSpecification : @abstractmethod def is_satisfied_by ( self , candidato : Any ) -> bool : raise NotImplementedError () def __call__ ( yo , candidato : Cualquiera ) -> bool : return is_satisfied_by ( candidato ) def __and__ ( self , other : "BaseSpecification" ) -> "AndSpecification" : return AndSpecification ( self , other ) def __or__ ( self , other : "BaseSpecification" ) -> "OrSpecification" : return OrSpecification ( self , other ) def __neg__ ( self ) -> "NotSpecification" : devuelve NotSpecification ( self )@dataclass ( congelado = True ) clase AndSpecification ( BaseSpecification ): primero : BaseSpecification segundo : BaseSpecification def is_satisfied_by ( self , candidato : Cualquiera ) -> bool : return self . primero . is_satisfied_by ( candidato ) y self . segundo . is_satisfied_by ( candidato )@dataclass ( congelado = True ) clase OrSpecification ( BaseSpecification ): primero : BaseSpecification segundo : BaseSpecification def is_satisfied_by ( self , candidato : Cualquiera ) -> bool : return self . primero . is_satisfied_by ( candidato ) o self . segundo . is_satisfied_by ( candidato )@dataclass ( congelado = True ) class NotSpecification ( BaseSpecification ): asunto : BaseSpecification def is_satisfied_by ( self , candidato : Any ) -> bool : return not self . sujeto . is_satisfied_by ( candidato )
C ++
plantilla < clase T > clase ISpecification { public : 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 > * O ( const ISpecification < T > & Other ) const = 0 ; virtual ISpecification < T > * OrNot ( const ISpecification < T > & Other ) const = 0 ; especificación virtual < T > * Not () const = 0 ; }; template < class T > class CompositeSpecification : public ISpecification < T > { public : virtual bool IsSatisfiedBy ( T Candidate ) const override = 0 ;virtual ISpecification < T > * Y ( const ISpecification < T > & Other ) const override ; virtual ISpecification < T > * AndNot ( const ISpecification < T > & Other ) const override ; virtual ISpecification < T > * O ( 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 > & Izquierda ; const ISpecification < T > & Derecha ;AndSpecification ( const ISpecification < T > e InLeft , const ISpecification < T > e InRight ) : Izquierda ( InLeft ), Derecha ( InRight ) { }virtual bool IsSatisfiedBy ( T Candidate ) const override { return Left . IsSatisfiedBy ( Candidate ) && Right . IsSatisfiedBy ( candidato ); } };template < class 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 > & Izquierda ; const ISpecification < T > & Derecha ;AndNotSpecification ( const ISpecification < T > e InLeft , const ISpecification < T > e InRight ) : Izquierda ( InLeft ), Derecha ( InRight ) { }virtual bool IsSatisfiedBy ( T Candidate ) const override { return Left . IsSatisfiedBy ( Candidate ) && ! Correcto . IsSatisfiedBy ( candidato ); } };plantilla < clase T > clase OrSpecification final : public CompositeSpecification < T > { public : const ISpecification < T > & Izquierda ; const ISpecification < T > & Derecha ;OEspecificación ( const ISpecification < T > e InLeft , const ISpecification < T > e InRight ) : Izquierda ( InLeft ), Derecha ( InRight ) { }virtual bool IsSatisfiedBy ( T Candidate ) const override { return Left . IsSatisfiedBy ( candidato ) || Correcto . IsSatisfiedBy ( candidato ); } };plantilla < clase T > clase OrNotSpecification final : public CompositeSpecification < T > { public : const ISpecification < T > & Izquierda ; const ISpecification < T > & Derecha ;OrNotSpecification ( const ISpecification < T > e InLeft , const ISpecification < T > e InRight ) : Izquierda ( InLeft ), Derecha ( InRight ) { }virtual bool IsSatisfiedBy ( T Candidate ) const override { return Left . IsSatisfiedBy ( candidato ) || ! Correcto . IsSatisfiedBy ( candidato ); } };plantilla < clase T > clase NotSpecification final : public CompositeSpecification < T > { public : const ISpecification < T > & Otro ;NotSpecification ( const ISpecification < T > e InOther ) : Otro ( InOther ) { }virtual bool IsSatisfiedBy ( T Candidate ) const override { return ! Otro . IsSatisfiedBy ( candidato ); } };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 > :: O ( 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 ); }plantilla < clase T > ISpecification < T > * CompositeSpecification < T > :: Not () const { return new NotSpecification < T > ( * this ); }
Ejemplo de uso
En el siguiente ejemplo, recuperamos facturas y las enviamos a una agencia de cobranza si
- están atrasados,
- se han enviado avisos, y
- aún no están con la agencia de cobranza.
Este ejemplo está destinado a mostrar el resultado final de cómo la lógica está 'encadenada'.
Este ejemplo de uso asume una clase OverdueSpecification previamente definida que se satisface cuando la fecha de vencimiento de una factura es de 30 días o más, una clase NoticeSentSpecification que se satisface cuando se han enviado tres avisos al cliente y una clase InCollectionSpecification que se satisface cuando se ha recibido una factura. ya ha sido enviado a la agencia de cobranza. La implementación de estas clases no es importante aquí.
Usando estas tres especificaciones, creamos una nueva especificación llamada SendToCollection que se cumplirá cuando una factura esté vencida, cuando se hayan enviado avisos al cliente y no estén ya en la agencia de cobranza.
var OverDue = new OverDueSpecification (); var NoticeSent = new NoticeSentSpecification (); var InCollection = new InCollectionSpecification ();// ejemplo de encadenamiento lógico de patrón de especificación var SendToCollection = OverDue . Y ( NoticeSent ). Y ( InCollection . Not ());var InvoiceCollection = Servicio . GetInvoices ();foreach ( var CurrentInvoice en InvoiceCollection ) { if ( SendToCollection . IsSatisfiedBy ( currentInvoice )) { currentInvoice . SendToCollection (); } }
Referencias
- Evans, Eric (2004). Diseño impulsado por dominio . Addison-Wesley. pag. 224.
enlaces externos
- Especificaciones de Eric Evans y Martin Fowler
- El patrón de especificación: un manual de 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