实现一个对象验证库系列 -- 1) 接口介绍以及总体思路概述 (请大神批评)

实现一个对象验证库系列 -- 1) 接口介绍以及总体思路概述 (请大神批评),第1张

概述前情回顾:上一篇 0) 目录以及库结构介绍简单描述了下库的代码结构本文将从接口部分阐述总体的思路1) 接口介绍以及总体思路概述如下图,我总共定义了10个Interface这些实际可分为两类:为了支持 前情回顾:

上一篇 0) 目录以及库结构介绍 简单描述了下库的代码结构

本文将从接口部分阐述总体的思路

1) 接口介绍以及总体思路概述

如下图,我总共定义了10个Interface

这些实际可分为两类:

为了支持 Fluent 语法格式而定义的各个创建者接口:IFluentRuleBuilderIRuleBuilderIRuleMessageBuilderIValIDateRuleBuilderIValIDatorBuilderIValIDatorSetter验证 *** 作涉及的规则、结果、验证调用接口的定义:IRuleSelectorIValIDateResultIValIDateRuleIValIDator

 

接下来我们首先阐述下验证使用方式的接口设计思路,

然后再介绍 Fluent 格式的规则设置方式的设计思路。

(1)验证使用方式的接口设计思路

我们首先考虑的用户的验证使用方式,而且我们是提供用户自行设置验证规则,不是只是提供一些固定的验证规则,

那么用户其实只是想给一个数据,然后拿到对应的结果就行

所以大致接口设想就是

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,IValIDateResult> ValIDateFunc { get; set; } // 之所以有这个属性,是为了便利地拓展check的logic,不必每个新的规则check方式都必须写一个ValIDateRule类	IValIDateResult ValIDate(ValIDateContext context);  // 提供规则调用的接口}
(2)Fluent 格式的规则设置方式的设计思路

上面我们以及思考到了如何保存验证规则,那么我们如何用 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,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>> 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) 验证器实现

总结

以上是内存溢出为你收集整理的实现一个对象验证库系列 -- 1) 接口介绍以及总体思路概述 (请大神批评)全部内容,希望文章能够帮你解决实现一个对象验证库系列 -- 1) 接口介绍以及总体思路概述 (请大神批评)所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/1213362.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-04
下一篇 2022-06-04

发表评论

登录后才能评论

评论列表(0条)

保存