CommonLibrary之Validation
CommnLibrary是我目前看到的写的最好的开源通用类库,七七八八包含了如 40个左右的通用模块,代码写的也非常的棒,现在找点时间来详细的学习,记录一些常用模块,以便替换以前的通用类库.
Validation
当我看到这个验证的模块的实现应用方式让我眼睛一亮,
Validation.IsNumeric("asdklf")
//参数1:文本值 参数2:能否为空 Validation.IsAlphaNumeric("asd123dsd43", false)
//参数1:文本值 参数2:能否为空,3,4,是否检验最大(小)长度,5,6最大(小)长度 Validation.IsStringLengthMatch("user01", false, true, true, 2, 12) //使用一个errors对象保存错误集合 var errors = new Errors(); Validation.IsAlpha("123abc", false, errors, ""); //使用lamda表达式进行验证 var val = new Validator(valEvent => { int errCount = valEvent.Results.Count; Validation.IsEmail("kishore@", false, valEvent.Results, string.Empty); Validation.IsUrl("http://www", false, valEvent.Results, string.Empty); Validation.IsPhoneUS("111-111-111", false, valEvent.Results, string.Empty); return errCount == valEvent.Results.Count; }); PrintErrors(val.Validate()); // 4.使所有的类型验证在一个对象中搞定 var val = new ValidatorWithRules(); val.Add(e => Validation.IsEmail("kishore@", false, e.Results, string.Empty)); val.Add(e => Validation.IsUrl("http://www", false, e.Results, string.Empty)); val.Add(e => Validation.IsPhoneUS("111-111-111", false, e.Results, string.Empty)); PrintErrors(val.Validate());
//5.使用链式调用进行验证 val.Check(() => user.UserName).IsNotNull().IsBetween(1, 50) .Check(() => user.CreateDate).IsAfterToday() .Check("邮箱", user.Email).IsValidEmail() .Check(() => user.MobilePhone).IsValidPhoneUS();
//6.一次执行多个自定义类的验证 结果保存在一个集合之中 var validators = new List<IValidator>() { new MyCustomUserIdValidator("admin"), new MyCustomUserIdValidator("batman") }; // Run all the validators and collect the errors. ValidationResults errors = new ValidationResults(); ValidationUtils.Validate(validators, errors);
看了上面几个方法,服务端的所需的验证都包含在了里面,接下来,从整体的设计到每种方法的实现来分析一下这个模块的实现:
Validation模块接口关系图:
最初看到这个,觉得这个功能模块设计的比较复杂,但是弄清楚了具体的关系,其实还是比较复杂。最开始从IErrors,Errors,IValidationResults,ValidationResults开始看.
IErrors接口定义了操作集合的一些方法和属性,Errors类实现该接口,通过一个Dictionary<string,string>,List<string>来保存错误信息,这里用了2个集合保持错误信息,Dictionary加上的原因是为了key对应验证的错误说明,Value对应错误值,比如说:key:用户名 value:不能为空
IValidationResults接口继承Ierrors接口,只有一个属性,bool IsValide,来控制验证是否通过,而ValidationResults继承了该接口和IErrors接口,里面只定义了一个readonly的自己对象本身,这样定义好处是不仅能使用得到Errors类的公有方法,而且加上了自己对错误集合(Errors类中的2个集合的)控制,使得错误集合不能被更改,这样的设计能最大限度的加大代码的灵活性和分工的明确性,但是我觉得太过繁琐,要是我设计的话会去掉IValidationResults接口和ValidationResults 类,在IError和Errors类中做控制:)
IValidatorStateful和IValidatorNoneStateful接口定义了验证方法,不同在于,NoneStateful不保存对象状态,这样做允许所有验证在一个方法中搞定
IValidator接口继承了IValidatorStateful和IValidatorNoneStateful接口,这里它只是一个空接口
IValidatorWithRules继承IValidator接口,这个接口用于定义自定义验证
Validator类,继承了IValidator这个接口,实现了IValidatorStateful,IValidatorNoneStateful的所有方法
ValidatorWithRules继承Validator类,实现IValidatorWithRules接口
IValidator为空接口,这里这样做多态的灵活性出来了,Validator类中,几个实现方法被定义成了虚方法,便于继承者重写整个接口的关系,我感觉设计的太过复杂,也是项目性质不一样,内部的复杂设计,带来的高度的灵活性和扩展性,封装成一个DLL,调用的人只要知道如何调用就行,通用类库维护人员来控制代码的扩展和维护,在平时做这样的设计,对程序对代码的理解水平要求有一点,在项目里面还不能大面积讨论,不然讨论个10天半个月最后定下来一个类就搞定,呵呵,扯远了。
这篇水文写到这已经3天时间了,犀利啊。每天晚上抽点时间写一点,为平淡的生活找点乐子吧。现在讨论具体的实现。今天看了景春雷同志写的在自己的项目中实现 Fluent Interface(流畅接口),春雷哥哥理解的蛮好的,学习。今天先写写在CommnLibrary中ValidatorFluent的实现吧,
ValidatorFluent
调用代码在最上面的用例5中,如Jquery的链式调用的语法写着很舒服,这样的语法格式能让代码简单,"高效",思维连贯。实现并不复杂,看看该类中的代码,所有方法返回this,便于链式调用的继续,让我感到吃惊的是作者在该类中加上了一个:
public ValidatorFluent End() { return this; }
用人的思考方式来书写供计算机阅读的代码,大大增加了代码的可读性,而我理解的扩展方法的作用一部分意义也在于此。除此之外,首先Check方法有3个重载,Check(Expression<Func<object>> exp)支持了表达式的解析,另外2个Check方法为什么不用泛型T来传参数?在这个类中,传递泛型意义不大,某些验证中所有类型还是要进行类型推断或者转换成特定类型(如:string); 在这个类中,结合了Validation类的验证,如果需要,还可以继续增加上IsDate,IsNumeric的Validation类的验证。所有的验证错误信息,全部放在前面说的ValidationResults类的集合中,进行合并处理。
太晚了,先睡了,明天在继续写,想什么都讲,结果发现很多地方只要看一眼就明白,不需要讲,也不知道此类文章该怎么写才能分享我的一些浅薄的认识,希望大家能提供点这方面意见。