Especificación (patrón de diseño)

Una "especificación" en programación es un patrón de diseño mediante el cual una representación de reglas de lógica empresarial se puede transformar en una cadena de objetos vinculados por operaciones lógicas booleanas .

Este patrón destaca aquellas especificaciones (reglas) en la lógica empresarial que son adecuadas para "acoplarse" con otras. El objeto de lógica empresarial hereda su funcionalidad de la clase CompositeSpecification de agregación abstracta, que contiene solo un método IsSatisfiedBy que devuelve un valor booleano. Una vez instanciado, un objeto se encadena con otros objetos. Como resultado, sin perder flexibilidad en la personalización de la lógica empresarial, podemos agregar fácilmente nuevas reglas.

Ejemplos de código

C#

interfaz pública ISpecification { bool IsSatisfiedBy ( objeto candidato ); ISpecification And ( ISpecification otro ); ISpecification O ( ISpecification otro ); ISpecificationNot ( ); } public abstract class CompositeSpecification : ISpecification { public abstract bool IsSatisfiedBy ( objeto candidato ); public ISpecification And ( ISpecification other ) { return new AndSpecification ( this , other ); } public ISpecification Or ( ISpecification other ) { return new OrSpecification ( this , other ); } public ISpecification Not () { return new NotSpecification ( este ); } } clase pública AndSpecification : CompositeSpecification { private ISpecification One ; privado ISpecification Otro ; public AndSpecification ( ISpecification x , ISpecification y ) { Uno = x ; Otro = y _ } public override bool IsSatisfiedBy ( objeto candidato ) { return One . IsSatisfiedBy ( candidato ) && Otro . EstáSatisfechoPor ( candidato ); } } clase pública OrSpecification : CompositeSpecification { private ISpecification One ; privado ISpecification Otro ; public OrSpecification ( ISpecification x , ISpecification y ) { Uno = x ; Otro = y _ } public override bool IsSatisfiedBy ( objeto candidato ) { return One . Está satisfecho por ( candidato ) || Otro _ EstáSatisfechoPor ( candidato ); } } clase pública NotSpecification : CompositeSpecification { private ISpecification Wrapped ; public NotSpecification ( ISpecification x ) { Wrapped = x ; } public override bool IsSatisfiedBy ( objeto candidato ) { return ! envuelto _ EstáSatisfechoPor ( candidato ); } }

C# 3.0 , simplificado a través de plantillas y métodos de extensión

interfaz pública ISpecification < TEntity > { bool IsSatisfiedBy ( entidad TEntity ); } clase interna AndSpecification < TEntity > : ISpecification < TEntity > { private readonly ISpecification < TEntity > _spec1 ; ISpecification privado de solo lectura < TEntity > _spec2 ; ISpecification protegida < TEntity > Spec1 { get { return _spec1 ; } } ISpecification protegida < TEntity > Spec2 { get { return _spec2 ; } } AndSpecification interna ( Especificación < TEntity > spec1 , ISpecification < TEntity > spec2 ) { if ( spec1 == null ) lanza una nueva excepción ArgumentNullException ( "spec1" ); if ( spec2 == null ) lanza una nueva ArgumentNullException ( "spec2" ); _espec1 = especificación1 ; _spec2 = spec2 ; } public bool IsSatisfiedBy ( candidato de TEntity ) { return Spec1 . IsSatisfiedBy ( candidato ) && Spec2 . EstáSatisfechoPor ( candidato ); } } clase interna OrSpecification < TEntity > : ISpecification < TEntity > { private readonly ISpecification < TEntity > _spec1 ; ISpecification privado de solo lectura < TEntity > _spec2 ; ISpecification protegida < TEntity > Spec1 { get { return _spec1 ; } } ISpecification protegida < TEntity > Spec2 { get { return _spec2 ; } } OrSpecification interna ( ISpecification < TEntity > spec1 , ISpecification < TEntity > spec2 ) { if ( spec1 == null ) throw new ArgumentNullException ( "spec1" ); if ( spec2 == null ) lanza una nueva ArgumentNullException ( "spec2" ); _espec1 = especificación1 ; _spec2 = spec2 ; } public bool IsSatisfiedBy ( candidato de TEntity ) { return Spec1 . Está satisfecho por ( candidato ) || especificación2 . EstáSatisfechoPor ( candidato ); } } clase interna NotSpecification < TEntity > : ISpecification < TEntity > { private readonly ISpecification < TEntity > _wrapped ; ISpecification protegida < TEntity > Wrapped { get { return _wrapped ; } } NotSpecification interno ( ISpecification < TEntity > spec ) { if ( spec == null ) { throw new ArgumentNullException ( "spec" ); } _envuelto = especificación ; } public bool IsSatisfiedBy ( candidato de TEntity ) { return ! envuelto _ EstáSatisfechoPor ( candidato ); } } ExtensionMethods de clase estática pública { ISpecification pública estática < TEntity > And < TEntity >( this ISpecification < TEntity > spec1 , ISpecification < TEntity > spec2 ) { return new AndSpecification < TEntity >( spec1 , spec2 ); } public static ISpecification < TEntity > Or < TEntity >( this ISpecification < TEntity > spec1 , ISpecification < TEntity > spec2 ) { return new OrSpecification < TEntity >( spec1 , spec2 ); } public static ISpecification < TEntity > Not < TEntity >( this ISpecification < TEntity > spec ) { return new NotSpecification < TEntity >( spec ); } }

Ejemplo de uso

En el siguiente ejemplo, revisamos las facturas y las enviamos a una agencia de cobranza si: están vencidas, aún no han sido enviadas a la agencia de cobranza y se ha enviado una advertencia al comprador. Este ejemplo muestra cómo las reglas están "encadenadas" entre sí.

El ejemplo se basa en tres especificaciones: OverdueSpecification, que se cumple si la factura se emitió hace más de 30 días, NoticeSentSpecification, que se cumple si se enviaron 3 advertencias al cliente, e InCollectionSpecification, que comprueba que la factura aún no se ha enviado. a la agencia de cobro. La implementación de estas clases no es tan importante.

Usando estas tres especificaciones, creamos una nueva regla SendToCollection que es verdadera si se cumplen las tres condiciones descritas en el párrafo anterior.

OverDueSpecification OverDue = new OverDueSpecification (); NoticeSentSpecification NoticeSent = new NoticeSentSpecification (); InCollectionSpecification InCollection = new InCollectionSpecification (); // ejemplo de reglas de encadenamiento ISpecification < Factura > SendToCollection = OverDue . Y ( AvisoEnviado ). Y ( EnColección . No ()); CobroFactura = Servicio . ObtenerFacturas (); foreach ( Factura factura actual en Cobro de facturas ) { if ( Enviar a cobro . IsSatisfiedBy ( factura actual )) { Factura actual . SendToCollection (); } }

Notas

Literatura

  • Evans, E: "Diseño impulsado por el dominio", página 224. Addison-Wesley, 2004.

Enlaces