2012-01-09 17 views
7

Voglio fare un sistema di validazione semplice per certa classe di oggetti, fondamentalmenteQuali sono le altre soluzioni per la seguente progettazione di regole?

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

L'elenco delle regole è rilevato automaticamente utilizzando quadro DI e non fissato in anticipo.

Diciamo che ho un VeryGeneralDefaultRuleAboutAllObjects : IMyClassRule e SpecificCaseWhenGeneralRuleDoesNotApply : IMyClassRule. Come posso gestire questa soluzione in modo generico (in pratica consentendo di ignorare qualsiasi regola da qualsiasi altra regola in determinati casi)?

Solutions I considerate:

  1. priorità numerico per le regole o le regole si traduce

    Pro: semplice da capire e da attuare.
    Contra: Avrò bisogno di conoscere/indovinare la priorità della regola originale. Non è ovvio quale priorità sia la prima (1 o 1000)? Richiede un risultato della regola "non mi interessa" per situazioni in cui il caso specifico non si applica. priorità basato

  2. Tipo (sostanzialmente .Before<VeryGeneralRule>)

    Pro: dichiara In particolare ciò che si vuole raggiungere.
    Contra: richiede un riferimento esplicito alla regola originale. La logica di ordinazione sarà complicata. Richiede un risultato della regola "non mi interessa" per situazioni in cui il caso specifico non si applica.

Ci sono altre/migliori opzioni?

+1

problema molto fastidioso. Ne ho discusso per ore e non ho trovato una soluzione soddisfacente. – CodesInChaos

+2

Considerare di rinunciare all'individuazione automatica e creare manualmente un elenco centrale di regole in ordine di priorità. – CodesInChaos

+0

Guarda il concetto di regola Web (http://rule.codeeffects.com). Penso che tu stia cercando qualcosa di simile a quello che fanno. – Kizz

risposta

0

Come aggiungere la possibilità di iniettare alcune condizioni nelle istanze delle regole, ad esempio, le istanze di un'interfaccia IRuleApplicability. Puoi combinarlo con qualcosa di simile al # 2 e usare una classe base di tutte le regole per verificare l'applicabilità prima di applicare la regola.

2

Penso che molto dipenderà dallo scopo del progetto e quanto si debba essere accoppiati liberamente. Lavoro molto sulle regole aziendali e devono essere il più estensibili possibile. Non mi legherei a un sistema di regole ordinali se c'è anche il minimo volume di regole, o il loro ordinamento è persino remotamente complesso. Penso che l'auto-scoperta/il cablaggio delle regole sia assolutamente il modo per andare qui.

La chiave per questo tipo di situazione, a mio avviso, è che le regole generali del caso sono non definite da un'assenza di logica correlata al loro ambito. Le regole del caso generale devono avere una logica dell'ambito tanto specifica quanto le regole del caso specifico. Possono essere inclusi 99 volte su 100, ma devono ancora avere una logica specifica per l'ambito.

Quanto segue è approssimativamente come mi avvicinerei a questo. Non sono entusiasta del fatto che WithinScope() sia collegato direttamente a IRule, ma dato che stai considerando una lista ordinale, presumo che la logica sia gestibile e relativamente statica, o che tu possa iniettare un delegato per quella logica.

Interfacce Framework

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(); 
} 

Generico Validator e regola fabbrica

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()); 
    } 
} 

regole di esempio

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; 
    } 
} 

IOC cablaggio (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

Ho recentemente implementato qualcosa che assomiglia molto a questo, meno il (più) sofisticato cablaggio StructureMap. Mi piace questa risposta perché convalida che ciò che ho fatto è visto come sano da almeno un'altra persona nella comunità. –

Problemi correlati