2009-08-21 7 views
7

Es gibt viele Fluent-Implementierungen, die jetzt mit Lambdas arbeiten, um Dinge zu tun, die ziemlich ordentlich sind. Ich möchte mein Gehirn darum herumschlingen, damit ich anfangen kann, einige dieser Dinge zu schaffen, aber ich muss noch eine Erklärung finden, die mein Gehirn versteht.Lambda Func <> und Fluent

Betrachten Sie dieses einfache Beispiel eines I Person Validator

public class PersonValidator : IValidator<Person> 
{ 
    public PersonValidator() 
    { 
      AddRule(p => p.FirstName).CannotBeNull().CannotBeBlank(); 
      AddRule(p => p.LastName).CannotBeNull().CannotBeBlank(); 
    } 

    public List<ValidationResult> Validate(Person p) 
    { 
     // pseudo... 
     apply all rules specified in constructor, return results 
    } 
} 

Ich habe es geschafft, Arbeitsteil von all dies zu erhalten, ein Verfahren auf meinem Validator wie dies mit ...

public ValidationResult<T,TProp> AddRule<T,TProp>(Func<T,TProp> property) 
{ 
    ... not sure what to do here. This method gives me the ability to use the lambda 
    ... for specifying which properties i want to validate 
} 

kann dann Erweiterungsmethoden erstellen, die IValidator für die Zwecke von CannotBeNull und CannotBeEmpty erweitern.

So scheint es, habe ich die erste Hälfte und die zweite Hälfte des Problems, aber ich bin mir nicht sicher, wie man sie zusammenbringt.

Auf der Suche nach einer sinnvollen Erklärung ... Ich möchte "Get it". :)

+0

Ihr Beispiel macht keinen Sinn, wenn Sie das tun addrule(). CannotBeNull(). CannotBeBlank() sagen, Sie möchten, diese Regeln zu Ihren Validierungsregeln hinzufügen und wende sie später? –

+0

Ja, genau. Ich möchte in der Lage sein, AddRule zu verwenden, gefolgt von einer beliebigen Anzahl von verketteten Methoden, die eine Validierung für eine bestimmte Eigenschaft der Klasse anwenden. Meine Herausforderung ist, dass ich nicht weiß, was ich in "AddRule" tun soll. Ich weiß, dass ich diese im Validator bestehen muss, aber ich weiß nicht, wie man das macht? – ctorx

Antwort

5

Der Schlüssel zu fließenden Schnittstellen ist, dass Methoden wie CannotBeNull() und CannotBeBlank() die aktuelle Instanz (d. H. Diese) zurückgeben. Wenn Sie möchten, dass Ihre AddRule-Methode "fließend" ist, müssen Sie anstelle von ValidationResult die aktuelle Instanz von IValidator zurückgeben. Ihre Erweiterungsmethoden müssen auch die Instanz von IValidator zurückgeben, die sie erweitern.

Ich denke, Ihre genaue Implementierung muss möglicherweise ein bisschen komplexer sein, und hoffentlich wird das folgende Beispiel einige Einblicke geben. Gleiche allgemeine Regel gilt jedoch ... return "this" eine fließend Schnittstelle zu erstellen:

interface IValidator<T> 
{ 
    IValidatorRule<T, TProp> AddRule<TProp>(Func<T, TProp> property); 
} 

interface IValidatorRule<T> 
{ 
    T instance { get; } 
    string PropertyName { get; } 

    ValidationResult Apply(T instance); 
} 

public static IValidatorAugmentorExtensions 
{ 
    public static IValidatorRule<T> CannotBeNull(this IValidatorRule<T> rule) 
    { 
     // ... 

     return rule; 
    } 

    public static IValidatorRule<T> CannotBeBlank(this IValidatorRule<T> rule) 
    { 
     // ... 

     return rule; 
    } 
} 

Die oben verwendet werden könnte, wie so:

public class PersonValidator: IValidator<Person> 
{ 
    public PersonValidator() 
    { 
     AddRule(p => p.FirstName).CannotBeNull().CannotBeEmpty(); 
     AddRule(p => p.LastName).CannotBeNull().CannotBeEmpty(); 
    }  

    public List<ValidationResult> Validate(Person p) 
    { 
     List<ValidationResult> results = new List<ValidationResult>(); 

     foreach (IValidatorRule<Person> rule in rules) // don't know where rules is, or what the AddRule method adds to...you'll need to figure that out 
     { 
      results = rule.Apply(p); 
     } 

     return results; 
    } 
} 

Während die oben zeigt, wie ein Fluent Interface erstellen Ich weiß nicht wirklich, was es dir in dieser besonderen Situation auf die Dauer bringt. Für die Bequemlichkeit einer flüssigen Schnittstelle, die scheinbar nur intern für konkrete Validatoren verwendet wird, haben Sie die Komplexität Ihres Codes um einen beträchtlichen Betrag erhöht, ohne wirklich eine nützliche, fließende Oberfläche für die Verbraucher Ihrer Validatoren bereitzustellen. Ich würde denken, dass Sie mehr Wert gewinnen würden, indem Sie den Entwicklern, die eine Validierung durchführen müssen, ein fließendes Validierungs-Framework zur Verfügung stellen, anstatt einen fließenden Rahmen für die Erstellung konkreter Validatoren bereitzustellen.

+0

+1 das ist, was ich schreiben wollte, aber du hast mich dazu geschlagen. Also habe ich einen anderen Ansatz gewählt. –

+0

Sie brauchen hier, was passiert mit "Regeln". Wie sieht die lokale Liste aus und wie wird sie genutzt? – ctorx

+0

Die einfachste Lösung wäre, Regeln als lokale Liste > zu haben.Die AddRule-Methode sollte die IValidatorRule erstellen und sie dieser Sammlung hinzufügen, und die Erweiterungsmethoden können verwendet werden, um diese Instanz über die flüssige Schnittstelle zu ändern. All dies beiseite, ich muss noch einmal betonen, dass ich denke, dass Sie viel Mühe für sehr wenig Gewinn aufwenden. Wenn Sie wirklich die Vorteile einer fließenden Oberfläche realisieren möchten, würde ich Ihr Validierungs-Framework überdenken. Anstatt konkrete Validatoren (d. H. PersonValidator) bereitzustellen, stellen Sie eine fließende Schnittstelle für diejenigen zur Verfügung, die die Validierung durchführen. – jrista

1

Jristas Antwort ist korrekt. Nur für einen anderen Ansatz ist hier, wie ich es geschafft habe.

public class PersonValidator : IValidator<Person> 
    { 
     List<Func<Person,bool>> validationRules = new List<Func<Person,bool>>(); 

    public PersonValidator() 
    { 
     AddRule(p => IsNullOrEmpty(p.FirstName)).AddRule(p1 => CheckLength(p1.FirstName)); 
    } 

    PersonValidator AddRule(Func<Person,bool> rule) 
    { 
     this.validationRules.Add(rule); 
     return this; 
    } 

    private bool IsNullOrEmpty(String stringToCheck) 
    { 
     return String.IsNullOrEmpty(stringToCheck); 
    } 

    private bool CheckLength(String stringToCheck) 
    { 
     return (String.IsNullOrEmpty(stringToCheck) ? false : stringToCheck.Length < 3); 
    } 

    #region IValidator<Person> Members 

    public bool Validate(Person obj) 
    { 
     return validationRules.Select(x => x(obj)).All(result => result == false); 
    } 

    #endregion 
} 



     Person test = new Person() { FirstName = null }; 
     Person test1 = new Person() { FirstName = "St" }; 
     Person valid = new Person() { FirstName = "John" }; 

     PersonValidator validator = new PersonValidator(); 
     Console.WriteLine("{0} {1} {2}", validator.Validate(test), validator.Validate(test1), validator.Validate(valid)); 
+0

Ich sehe nicht, wie dieses Beispiel diese Verwendung erleichtern würde ... AddRule (x => x.FirstName) .IsNullOrEmpty(); – ctorx

+1

wäre es nicht, weil es eine andere Herangehensweise wäre, ich wollte nur meinen Code beenden, anstatt ihn nur zu vergessen, nur weil jemand anders vor mir geantwortet hat. –

Verwandte Themen