2012-01-09 13 views
7

Quiero hacer un sistema de validación simple para cierta clase de objetos, básicamente¿Cuáles son las otras soluciones para el siguiente diseño de reglas?

public interface IMyClassRule { 
    bool IsValid(MyClass obj, Context someAdditionalData) 
} 

La lista de reglas es usando marco DI y no se fija de antemano descubierto automático.

Digamos que tengo un VeryGeneralDefaultRuleAboutAllObjects : IMyClassRule y SpecificCaseWhenGeneralRuleDoesNotApply : IMyClassRule. ¿Cómo puedo manejar esta solución de una manera genérica (básicamente permitiendo la anulación de cualquier regla por cualquier otra regla en ciertos casos)?

soluciones que consideran:

  1. prioridad numérico de normas o reglas resulta

    Pro: Fácil de entender y aplicar.
    Contra: Necesitaré saber/adivinar la prioridad de la regla original. ¿No es obvio qué prioridad es la primera (1 o 1000)? Necesita un resultado de regla de "no me importa" para una situación donde el caso específico no se aplica. prioridad basada

  2. Tipo (básicamente .Before<VeryGeneralRule>)

    Pro: declara específicamente lo que quiere lograr.
    Contra: Necesita una referencia explícita a la regla original. La lógica de pedidos será complicada. Necesita un resultado de regla de "no me importa" para una situación donde el caso específico no se aplica.

¿Hay alguna otra/mejores opciones?

+1

muy molesto problema. Lo he debatido durante horas y no he llegado a una solución satisfactoria. – CodesInChaos

+2

Considere renunciar al autodescubrimiento y cree manualmente una lista central de reglas en orden de prioridad. – CodesInChaos

+0

Mire el concepto de regla web (http://rule.codeeffects.com). Creo que estás buscando algo similar a lo que hacen. – Kizz

Respuesta

0

¿Qué le parece agregar la capacidad de inyectar algunas condiciones en las instancias de reglas, por ejemplo, instancias de una interfaz IRuleApplicability? Podría combinar eso con algo similar al # 2 y usar una clase base de todas las reglas para verificar la aplicabilidad antes de aplicar la regla.

2

Creo que mucho de esto dependerá del alcance del proyecto y de lo poco acoplado que sea necesario. Trabajo mucho en torno a las reglas comerciales, y deben ser lo más extensibles posible. No me vincularía a un sistema de reglas ordinales si existe el más mínimo volumen de reglas, o el orden de las mismas es remotamente complejo. Creo que el autodescubrimiento/cableado de las reglas es absolutamente el camino a seguir aquí.

La clave para este tipo de situación, en mi opinión, es que las reglas de casos generales son no definidas por una ausencia de lógica relacionada con su alcance. Las reglas de casos generales deben tener una lógica de alcance tan específica como las reglas de casos específicos. Pueden estar en el alcance 99 de 100 veces, pero aún necesitan tener una lógica de alcance específica.

Lo siguiente es más o menos cómo abordaría esto. No me entusiasma que WithinScope() esté conectado directamente a IRule, pero dado que está considerando una lista ordinal, asumo que la lógica es manejable y relativamente estática, o que podría inyectar un delegado para esa lógica.

Interfaces marco

public interface IRule<in T>{ 
    bool IsValid(T obj); 
    bool WithinScope(); 
} 

public interface IValidator<in T>{ 
    bool IsValid(T obj); 
} 

public interface IRuleFactory<in T>{ 
    IEnumerable<IRule<T>> BuildRules(); 
} 

Genérico Validador y la Regla de fábrica

public class GenericValidator<T> : IValidator<T> 
{ 
    private readonly IEnumerable<IRule<T>> _rules; 

    public GenericValidator(IRuleFactory<T> ruleFactory){ 
     _rules = ruleFactory.BuildRules(); 
    } 

    public bool IsValid(T obj){ 
     return _rules.All(p => p.IsValid(obj)); 
    } 
} 

public class GenericRuleFactory<T> : IRuleFactory<T> 
{ 
    private readonly IEnumerable<IRule<T>> _rules; 

    public GenericRuleFactory(IEnumerable<IRule<T>> rules){ 
     _rules = rules; 
    } 

    public IEnumerable<IRule<T>> BuildRules(){ 
     return _rules.Where(x => x.WithinScope()); 
    } 
} 

reglas de ejemplo

public class VeryGeneralDefaultRuleAboutAllObjects : IRule<IMyClass> 
{ 
    private readonly Context _context; 

    public VeryGeneralDefaultRuleAboutAllObjects(Context context){ 
     _context = context;  
    } 

    public bool IsValid(IMyClass obj){ 
     return !obj.IsAllJackedUp; 
    } 

    public bool WithinScope(){ 
     return !_context.IsSpecialCase; 
    } 
} 

public class SpecificCaseWhenGeneralRuleDoesNotApply : IRule<IMyClass> 
{ 
    private readonly Context _context; 

    public VeryGeneralDefaultRuleAboutAllObjects(Context context){ 
     _context = context;  
    } 

    public bool IsValid(IMyClass obj){ 
     return !obj.IsAllJackedUp && _context.HasMoreCowbell; 
    } 

    public bool WithinScope(){ 
     return _context.IsSpecialCase; 
    } 
} 

COI cableado (Usando StructureMap)

public static class StructureMapBootstrapper 
{ 
    public static void Initialize() 
    { 
     ObjectFactory.Initialize(x => 
     { 
      x.Scan(scan => 
      { 
       scan.TheCallingAssembly(); 
       scan.AssembliesFromApplicationBaseDirectory(); 
       scan.AddAllTypesOf(typeof (IRule<>)); 
      }); 

      x.For(typeof(IValidator<>)) 
       .Use(typeof(GenericValidator<>)); 

      x.For(typeof(IRuleFactory<>)) 
       .Use(typeof(GenericRuleFactory<>)); 
     }); 
    } 
} 
+0

Recientemente implementé algo que se parece mucho a esto, menos el cableado (más) sofisticado de StructureMap. Me gusta esta respuesta porque valida que lo que hice fue visto como sano por al menos otra persona en la comunidad. –

Cuestiones relacionadas