上一篇 0) 目錄以及庫結構介紹 簡單描述了下庫的代碼結構
本文將從接口部分闡述總體的思路
如下圖,我總共定義了10個Interface
這些實際可分為兩類:
接下來我們首先闡述下驗證使用方式的接口設計思路,
然后再介紹 Fluent 格式的規則設置方式的設計思路。
我們首先考慮的用戶的驗證使用方式,而且我們是提供用戶自行設置驗證規則,不是只是提供一些固定的驗證規則,
那么用戶其實只是想給一個數據,然后拿到對應的結果就行
所以大致接口設想就是
public interface IValidator{ object Validate(object data);}
方法是這樣,用戶拿到返回值怎么方便使用呢?
思考一番,思路都是類似如下的
public interface IValidateResult{ bool IsValid { get; } // 使用者肯定首先在乎驗證時候通過 List<ValidateFailure> Failures { get; } // 如果不通過,會有什么錯誤信息}// 錯誤信息的類,使用者一般都是關注那個地方出錯,出了什么錯,原始的值是什么才導致了錯誤public class ValidateFailure{ public string Name { get; set; } public object Value { get; set; } public string Error { get; set; }}
// 所以接口可以改成這樣public interface IValidator{ IValidateResult Validate(object data);}
思考一下參數,object肯定不是滿足的:
public class ValidateContext{ public IRuleSelector RuleSelector { get; set; } // 很多時候,用戶可能只是使用其中一些驗證規則,比如驗證學生信息時,大學生和幼兒園同學肯定是不一樣的 // 所以用戶得有一個實現如何選擇驗證規則方式的規則選擇器 public IEnumerable<string> RuleSetList { get; set; } // 大部分時候,規則選擇器都是一樣,但是規則選擇只需用戶設置一些標志,一個默認實現的規則選擇器基本夠用了 public ValidateOption Option { get; set; } // 有時候用戶可能需要全部錯誤,但是有時會先check null,不為null才做其他驗證 public object ValidateObject { get; set; }}public enum ValidateOption{ StopOnFirstFailure, Continue}public interface IRuleSelector{ bool CanExecute(IValidateRule rule, ValidateContext context);}// 所以最終接口這樣就可以了public interface IValidator{ IValidateResult Validate(ValidateContext context);}
接口方法這樣可以了,但是驗證規則如何保存呢?思考如下:
public interface IValidateRule{ string RuleSet { get; set; } // 規則分組的標志,大多數情況就可以滿足用戶對一個對象不同情境的驗證分組 IValidateRule NextRule { get; set; } // 一個規則與其他規則常有關聯性,比如先check 為null,然后再check 長度,都放在一個class中肯定不方便我們定義與使用 string ValueName { get; set; } // 有check 數據的名字屬性,這樣用戶可以改變這個名字 string Error { get; set; } // 有展示錯誤的屬性,這樣用戶可以改變這個屬性值 Func<ValidateContext, bool> Condition { get; set; } // 規則分組如果不滿足用戶,就只能提供這樣的func讓用戶自行篩選了 Func<ValidateContext, string, string, IValidateResult> ValidateFunc { get; set; } // 之所以有這個屬性,是為了便利地拓展check的logic,不必每個新的規則check方式都必須寫一個ValidateRule類 IValidateResult Validate(ValidateContext context); // 提供規則調用的接口}
上面我們以及思考到了如何保存驗證規則,那么我們如何用 Fluent 的方式設置規則呢?
首先我們拋開 Fluent 的理念,想一下我們如何創建規則呢?
是不是這樣呢?
new ValidateRule() { RuleSet = "xx", ValueName = "xx" .....}
回憶一下 Fluent 的方式,鏈式的語法,而且在我們這里用來設置驗證規則,簡直就是創建者模式的鏈式使用而已
ValidateRule.SetRuleSet("xx").SetValueName("xx")
所以如下我們有了規則的創建者
public interface IValidateRuleBuilder{ string RuleSet { get; set; } string ValueName { get; set; } string Error { get; set; } Func<ValidateContext, bool> Condition { get; set; } IValidateRuleBuilder NextRuleBuilder { get; set; } Func<ValidateContext, string, string, IValidateResult> ValidateFunc { get; set; } IValidateRule Build(); // 大致與IValidateRule差不多,只是多了這個Build 方法}public interface IValidatorBuilder<T>{ IFluentRuleBuilder<T, TPRoperty> RuleFor<TProperty>(Expression<Func<T, TProperty>> expression); void RuleSet(string ruleSet, Action<IValidatorBuilder<T>> action); IValidator Build();}
Fluent 設計時都是大致設想最終效果類似什么樣,才能建立對應的接口這些定義
我們回想一下我們的最終效果是什么樣呢?
ValidatorBuilder.RuleFor(i => i.Age) .Must(i => i >= 0 && i <= 18) .OverrideName("student age") .OverrideError("not student") .ThenRuleFor(i => i.Name) .Must(i => !string.IsNullOrWhiteSpace(i)) .OverrideName("student name") .OverrideError("no name");
解釋一下我們最終的效果:
ValidatorBuilder.RuleFor(i => i.Age) //一個 ValidatorBuilder 才能創建父級驗證規則,并會設置對應驗證的屬性 .Must(i => i >= 0 && i <= 18) // 每個規則必須設置如何驗證方法 .OverrideName("student age") .OverrideError("not student") // 設置一些需要復寫的信息 .ThenRuleFor(i => i.Name) // 只有設置完了一個規則的驗證方法之后才能建立子級規則 .Must(i => !string.IsNullOrWhiteSpace(i)) .OverrideName("student name") .OverrideError("no name");
總體來說我們將一個規則的設置分成了幾個階段,并且這些階段是不可逆的,有著嚴格順序的
如此我們的接口便會是如下:
public interface IValidatorBuilder<T>{ IFluentRuleBuilder<T, TProperty> RuleFor<TProperty>(Expression<Func<T, TProperty>> expression); void RuleSet(string ruleSet, Action<IValidatorBuilder<T>> action); IValidator Build();}// 對應 初始創建父級驗證規則,并會設置對應驗證的屬性 這個階段public interface IFluentRuleBuilder<T, TProperty>{}// 對應 設置如何驗證方法 這個階段public interface IRuleMessageBuilder<T, TValue>{ IFluentRuleBuilder<T, TProperty> ThenRuleFor<TProperty>(Expression<Func<T, TProperty>> expression);}// 對應 填寫一些描述信息或者建立子級規則 這個階段public interface IRuleBuilder<T, TValue> : IValidateRuleBuilder, IRuleMessageBuilder<T, TValue>, IFluentRuleBuilder<T, TValue>{ Func<object, TValue> ValueGetter { get; } Expression<Func<T, TValue>> ValueExpression { get; } void SetValueGetter(Expression<Func<T, TValue>> expression);}// 總結三個階段的最終定義
以上全部就是接口的設計思路
后面將慢慢的描述如何實現
NEXT: 2) 驗證器實現
新聞熱點
疑難解答