En programación informática, el patrón de especificación es un patrón de diseño de software particular , mediante el cual las reglas de negocio se pueden recombinar encadenándolas entre sí mediante lógica booleana . El patrón se utiliza con frecuencia en el contexto del diseño orientado al dominio .
Un patrón de especificación describe 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 de especificación compuesta agregada abstracta. La clase de especificación compuesta 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 que la lógica de negocio sea altamente personalizable. Además, tras la instanciación, la lógica de negocio puede, a través de la invocación de un método o la inversión de control , tener su estado alterado para convertirse en un delegado de otras clases, como un repositorio de persistencia.
Como consecuencia de la realización de 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 criterios de búsqueda de usuarios ad hoc en lógica de bajo nivel para que la procesen los repositorios.
Dado que una especificación es una encapsulación de lógica en una forma reutilizable, es muy simple realizar pruebas unitarias exhaustivas y, cuando se utiliza en este contexto, también es una implementación del patrón de objeto humilde.
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 (); } clase abstracta pública CompositeSpecification : ISpecification { bool abstracto público IsSatisfiedBy ( objeto candidato ); public ISpecification And ( ISpecification otro ) { return new AndSpecification ( este , otro ); } público ISpecification AndNot ( ISpecification otro ) { devolver nuevo AndNotSpecification ( este , otro ); } public ISpecification Or ( ISpecification otro ) { return new OrSpecification ( este , otro ); } público ISpecification OrNot ( ISpecification otro ) { devolver nuevo OrNotSpecification ( este , otro ); } public ISpecification Not () { devolver nueva NotSpecification ( this ); } } clase pública AndSpecification : CompositeSpecification { ISpecification privada condiciónIzquierda ; ISpecification privada condiciónDerecha ; public AndSpecification ( ISpecification izquierda , ISpecification derecha ) { condiciónIzquierda = izquierda ; condiciónDerecha = derecha ; } público anular bool IsSatisfiedBy ( objeto candidato ) { devolver leftCondition . IsSatisfiedBy ( candidato ) && rightCondition . IsSatisfiedBy ( candidato ); } } clase pública AndNotSpecification : CompositeSpecification { ISpecification privada condiciónIzquierda ; ISpecification privada condiciónDerecha ; public AndNotSpecification ( ISpecification izquierda , ISpecification derecha ) { condiciónIzquierda = izquierda ; condiciónDerecha = derecha ; } público anular bool IsSatisfiedBy ( objeto candidato ) { devolver leftCondition . IsSatisfiedBy ( candidato ) && rightCondition . IsSatisfiedBy ( candidato ) != true ; } } clase pública OrSpecification : CompositeSpecification { ISpecification privada condiciónIzquierda ; ISpecification privada condiciónDerecha ; public OrSpecification ( ISpecification izquierda , ISpecification derecha ) { condiciónIzquierda = izquierda ; condiciónDerecha = derecha ; } público anular bool IsSatisfiedBy ( objeto candidato ) { devolver leftCondition . IsSatisfiedBy ( candidato ) || rightCondition . IsSatisfiedBy ( candidato ); } } clase pública OrNotSpecification : CompositeSpecification { ISpecification privada condiciónIzquierda ; ISpecification privada condiciónDerecha ; público OrNotSpecification ( ISpecification izquierda , ISpecification derecha ) { condiciónIzquierda = izquierda ; condiciónDerecha = derecha ; } público anular bool IsSatisfiedBy ( objeto candidato ) { devolver leftCondition . IsSatisfiedBy ( candidato ) || rightCondition . IsSatisfiedBy ( candidato ) != true ; } } clase pública NotSpecification : CompositeSpecification { privada ISpecification Wrapped ; público NotSpecification ( ISpecification x ) { Envuelto = x ; } público anular bool IsSatisfiedBy ( objeto candidato ) { devolver ! Wrapped . IsSatisfiedBy ( candidato ); } }
interfaz pública ISpecification < T > { bool IsSatisfiedBy ( T candidato ); ISpecification < T > And ( ISpecification < T > otro ); ISpecification < T > AndNot ( ISpecification < T > otro ); ISpecification < T > Or ( ISpecification < T > otro ); ISpecification < T > OrNot ( ISpecification < T > otro ); ISpecification < T > Not (); } clase abstracta pública LinqSpecification < T > : CompositeSpecification < T > { expresión abstracta pública < Func < T , bool >> AsExpression (); bool público anulado IsSatisfiedBy ( T candidato ) => AsExpression (). Compile () ( candidato ); } clase abstracta pública CompositeSpecification < T > : ISpecification < T > { bool abstracto público IsSatisfiedBy ( T candidato ); ISpecification pública < T > And ( ISpecification < T > otro ) => nuevo AndSpecification < T > ( este , otro ); ISpecification pública < T > AndNot ( ISpecification < T > otro ) => nuevo AndNotSpecification < T > ( este , otro ); ISpecification pública < T > Or ( ISpecification < T > otro ) => nuevo OrSpecification < T > ( este , otro ); ISpecification pública < T > OrNot ( ISpecification < T > otro ) => nuevo OrNotSpecification < T > ( este , otro ); ISpecification pública < T > Not () => nuevo NotSpecification < T > ( este ); } clase pública AndSpecification < T > : CompositeSpecification < T > { ISpecification < T > izquierda ; ISpecification < T > derecha ; public AndSpecification ( ISpecification < T > izquierda , ISpecification < T > derecha ) { this . left = izquierda ; this . right = derecha ; } público anular bool IsSatisfiedBy ( T candidato ) => izquierda . IsSatisfiedBy ( candidato ) && derecha . IsSatisfiedBy ( candidato ); } clase pública AndNotSpecification < T > : CompositeSpecification < T > { ISpecification < T > izquierda ; ISpecification < T > derecha ; public AndNotSpecification ( ISpecification < T > izquierda , ISpecification < T > derecha ) { este . izquierda = izquierda ; este . derecha = derecha ; } público anular bool IsSatisfiedBy ( T candidato ) => izquierda . IsSatisfiedBy ( candidato ) && ! derecha . IsSatisfiedBy ( candidato ); } clase pública OrSpecification < T > : CompositeSpecification < T > { ISpecification < T > izquierda ; ISpecification < T > derecha ; public OrSpecification ( ISpecification < T > izquierda , ISpecification < T > derecha ) { this . left = izquierda ; this . right = derecha ; } public override bool IsSatisfiedBy ( T candidato ) = > izquierda.IsSatisfiedBy ( candidato ) || derecha.IsSatisfiedBy ( candidato ) ; } public class OrNotSpecification <T> : CompositeSpecification <T> { ISpecification <T> izquierda ; ISpecification <T> derecha ; public OrNotSpecification ( ISpecification < T > izquierda , ISpecification < T > derecha ) { este . izquierda = izquierda ; este . derecha = derecha ; } public override bool IsSatisfiedBy ( T candidato ) = > izquierda.IsSatisfiedBy ( candidato ) || ! derecha.IsSatisfiedBy ( candidato ) ; } clase pública NotSpecification < T > : CompositeSpecification < T > { ISpecification < T > otro ; pública NotSpecification ( ISpecification < T > otro ) => este . otro = otro ; pública anulación bool IsSatisfiedBy ( T candidato ) => ! otro . IsSatisfiedBy ( candidato ); }
desde abc import ABC , método abstracto desde clases de datos importar clase de datos desde tipificación importar Cualquieraclase BaseSpecification ( ABC ): @abstractmethod def is_satisfied_by ( self , candidato : Any ) -> bool : generar NotImplementedError () def __call__ ( self , candidato : Cualquiera ) -> bool : return self . 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 ) -> "NoEspecificación" : devuelve NoEspecificación ( self )@dataclass ( frozen = True ) clase AndSpecification ( BaseSpecification ): primero : BaseSpecification segundo : BaseSpecification def is_satisfied_by ( self , candidato : Cualquiera ) -> bool : devuelve self . first . is_satisfied_by ( candidato ) y self . second . is_satisfied_by ( candidato )@dataclass ( frozen = True ) clase OrSpecification ( BaseSpecification ): primero : BaseSpecification segundo : BaseSpecification def is_satisfied_by ( self , candidato : Cualquiera ) -> bool : devuelve self . first . is_satisfied_by ( candidato ) o self . second . is_satisfied_by ( candidato )@dataclass ( frozen = True ) clase NotSpecification ( BaseSpecification ): asunto : BaseSpecification def is_satisfied_by ( self , candidato : Cualquiera ) -> bool : devuelve not self . subject . is_satisfied_by ( candidato )
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 : pública ISpecification < T > { pública : virtual bool IsSatisfiedBy ( T Candidate ) const override = 0 ; virtual ISpecification < T >* And ( const ISpecification < T >& Other ) const anulación ; virtual ISpecification < T >* AndNot ( const ISpecification < T >& Other ) const anulación ; virtual ISpecification < T >* Or ( const ISpecification < T >& Other ) const anulación ; virtual ISpecification < T >* OrNot ( const ISpecification < T >& Other ) const anulación ; virtual ISpecification < T >* Not () const anulación ; }; plantilla < clase T > clase AndSpecification final : público CompositeSpecification < T > { público : const ISpecification < T >& Izquierda ; const ISpecification < T >& Derecha ; YEspecificación ( const ISpecification < T >& InLeft , const ISpecification < T >& InRight ) : Izquierda ( InLeft ), Derecha ( InRight ) { } bool virtual IsSatisfiedBy ( T Candidate ) const anular { devolver Izquierda . IsSatisfiedBy ( Candidate ) && Derecha . IsSatisfiedBy ( Candidate ); } }; plantilla < class T > ISpecification < T >* CompositeSpecification < T >:: Y ( const ISpecification < T >& Otro ) const { return new AndSpecification < T > ( * this , Otro ); } plantilla < clase T > clase AndNotSpecification final : público CompositeSpecification < T > { público : const ISpecification < T >& Izquierda ; const ISpecification < T >& Derecha ; AndNotSpecification ( const ISpecification < T >& InLeft , const ISpecification < T >& InRight ) : Izquierda ( InLeft ), Derecha ( InRight ) { } bool virtual IsSatisfiedBy ( T Candidate ) const anular { devolver Izquierda . IsSatisfiedBy ( Candidate ) && ! Derecha . IsSatisfiedBy ( Candidate ); } }; plantilla < clase T > clase OrSpecification final : público CompositeSpecification < T > { público : const ISpecification < T >& Izquierda ; const ISpecification < T >& Derecha ; OrSpecification ( const ISpecification < T >& InLeft , const ISpecification < T >& InRight ) : Izquierda ( InLeft ), Derecha ( InRight ) { } virtual bool IsSatisfiedBy ( T Candidate ) const anular { devolver Izquierda . IsSatisfiedBy ( Candidate ) || Derecha . IsSatisfiedBy ( Candidate ); } }; plantilla < clase T > clase OrNotSpecification final : público CompositeSpecification < T > { público : const ISpecification < T >& Izquierda ; const ISpecification < T >& Derecha ; OrNotSpecification ( const ISpecification < T >& InLeft , const ISpecification < T >& InRight ) : Izquierda ( InLeft ), Derecha ( InRight ) { } bool virtual IsSatisfiedBy ( T Candidate ) const anular { devolver Izquierda . IsSatisfiedBy ( Candidate ) || ! Derecha . IsSatisfiedBy ( Candidate ); } }; plantilla < clase T > clase NotSpecification final : público CompositeSpecification < T > { público : const ISpecification < T >& Otro ; NoEspecificación ( const IEspecificación < T >& InOther ) : Otro ( InOther ) { } bool virtual IsSatisfiedBy ( T Candidate ) const anular { devolver ! Otro . IsSatisfiedBy ( Candidate ); } }; plantilla < class T > ISpecification < T >* CompositeSpecification < T >:: AndNot ( const ISpecification < T >& Other ) const { return new AndNotSpecification < T > ( * this , Other ); } plantilla < class T > ISpecification < T >* CompositeSpecification < T >:: Or ( const ISpecification < T >& Other ) const { return new OrSpecification < T > ( * this , Other ); } plantilla < class T > ISpecification < T >* CompositeSpecification < T >:: OrNot ( const ISpecification < T >& Other ) const { return new OrNotSpecification < T > ( * this , Other ); } plantilla < class T > ISpecification < T >* CompositeSpecification < T >:: Not () const { return new NotSpecification < T > ( * this ); }
exportar interfaz ISpecification { isSatisfiedBy ( candidato : desconocido ) : booleano ; y ( otro : ISpecification ) : ISpecification ; andNot ( otro : ISpecification ) : ISpecification ; o ( otro : ISpecification ) : ISpecification ; orNot ( otro : ISpecification ) : ISpecification ; no () : ISpecification ; } exportar clase abstracta CompositeSpecification implementa ISpecification { abstracto isSatisfiedBy ( candidato : desconocido ) : booleano ; y ( otro : ISpecification ) : ISpecification { return new AndSpecification ( este , otro ); } andNot ( otro : ISpecification ) : ISpecification { devolver nuevo AndNotSpecification ( este , otro ); } o ( otro : ISpecification ) : ISpecification { devolver nueva OrSpecification ( este , otro ); } orNot ( otro : ISpecification ) : ISpecification { devolver nuevo OrNotSpecification ( este , otro ); } no () : ISpecification { devolver nuevo NotSpecification ( this ); } } clase de exportación AndSpecification extiende 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 ); } } clase de exportación AndNotSpecification extiende 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 ; } } clase de exportación OrSpecification extiende 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 ); } } clase de exportación OrNotSpecification extiende 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 ; } } clase de exportación NotSpecification extiende CompositeSpecification { constructor ( private wrap : ISpecification ) { super (); } isSatisfiedBy ( candidato : desconocido ) : booleano { return ! this . wrapper . isSatisfiedBy ( candidato ); } }
En el siguiente ejemplo, las facturas se recuperan y se envían a una agencia de cobranzas si:
Este ejemplo pretende mostrar el resultado de cómo se "encadena" la lógica.
Este ejemplo de uso supone una clase definida previamente OverdueSpecification
que se satisface cuando la fecha de vencimiento de una factura es de 30 días o más, una NoticeSentSpecification
clase que se satisface cuando se han enviado tres avisos al cliente y una InCollectionSpecification
clase que se satisface cuando ya se ha enviado una factura a la agencia de cobros. La implementación de estas clases no es importante aquí.
Utilizando 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 aún no estén en la agencia de cobranza.
var overDue = nueva EspecificaciónDeOverDue (); var noticeSent = nueva EspecificaciónDeNotificationSent (); var inCollection = nueva EspecificaciónDeInCollection (); // Ejemplo de encadenamiento de lógica de patrones de especificación var sendToCollection = overDue . And ( noticeSent ). And ( inCollection . Not ()); var invoiceCollection = Servicio . GetInvoices (); foreach ( var facturaActual en ColecciónFactura ) { if ( sendToCollection.IsSatisfiedBy ( facturaActual ) ) { facturaActual.SendToCollection ( ) ; } }