内容简介:在skinny.validator框架中,提供如下的一种DSL调用方式来验证Map的值:通过这种方式既可以将对应的DSL方法进行归类,又可以很好地支持DSL的编码形式,算是DSL的一种最佳实践。
在skinny.validator框架中,提供如下的一种DSL调用方式来验证Map的值:
def validationRules = Validator( param("name" -> name) is notEmpty, param("dataSourceId" -> dataSourceId) is checkAll(intValue, intMinValue(0)), param("sql" -> sql) is notEmpty, param("fields" -> fields) is checkAll(notNull, notEmpty) )
param()
方法返回的类型为 ParamDefinition
。 is
之类的方法看起来应该是 ParamDefinition
的DSL方法。不过在实现中,却提供了一个私有类来定义这些DSL方法,然后再定义一个隐式转换将私有类转换为 ParamDefinition
:
/** * Param definition which has #is and #are DSL methods. * * @param paramDef param definition */ private[validator] class ParamDefinitionWithIsDSL(paramDef: ParamDefinition) { def is(validations: ValidationRule): NewValidation = NewValidation(paramDef, validations) def are(validations: ValidationRule): NewValidation = NewValidation(paramDef, validations) } /** * Converts ParamDefinition to ParamDefinitionWithIsDSL implicitly. * * @param paramDef param definition * @return with dsl */ implicit def convertParamDefinitionToParamDefinitionWithIs(paramDef: ParamDefinition): ParamDefinitionWithIsDSL = { new ParamDefinitionWithIsDSL(paramDef) }
通过这种方式既可以将对应的DSL方法进行归类,又可以很好地支持DSL的编码形式,算是DSL的一种最佳实践。
is
或 are
等DSL方法接收的参数类型为 ValidationRule
(因为Scala允许以空格而非括号形式去调用,从而能够以自然语言的方式来表达)。 ValidationRule
的定义比较特殊,它本身是一个trait,但却继承自一个函数:
trait ValidationRule extends ((KeyValueParamDefinition) => ValidationState) with ErrorLike {}
看起来比较奇怪,其实不过是一种语法糖,因为在Scala中,函数就是一个定义了 apply()
方法的接口。这里之所以直接继承一个函数,倒也不是卖弄语法,而是因为作者压根就不想为这个函数定义一个类型。这也是FP的好处,因为函数自身就已经表达了一种最高层抽象,为什么还要多此一举去定义一个类型呢?对于一些不支持函数为一等公民的语言,才会这样行无奈之举。例如我需要抽象这么一个函数:
() => Unit
在 Java 语言中,由于不支持函数作为参数或返回值,那就只有无奈地寻找到一个所谓Action的概念,定义接口:
public interface Action { void execute(); }
ValidationRule
的定义实则是一个模板方法模式,该类的 apply()
方法就是模板方法(template method),而 isValid()
方法就是基本方法(primitive method),交给实现 ValidationRule
的类去具体实现:
trait ValidationRule extends ((KeyValueParamDefinition) => ValidationState) with ErrorLike { /** * Validation itself. * * @param value any value * @return valid if true */ def isValid(value: Any): Boolean /** * Applies this validation rule to parameter. * * @param paramDef param definition * @return validation */ def apply(paramDef: KeyValueParamDefinition): ValidationState = { if (isValid(paramDef.value)) ValidationSuccess(paramDef) else ValidationFailure(paramDef, Seq(Error(this.name, this.messageParams))) } }
skinny.validator框架提供了诸多内建的验证规则,并以case class或object(如果为单例)形式定义:
object notNull extends ValidationRule { def name = "notNull" def isValid(v: Any) = v != null } // param("x" -> "y") is required object required extends required(true) case class required(trim: Boolean = true) extends ValidationRule { def name = "required" def isValid(v: Any) = !isEmpty(v) && { if (trim) v.toString.trim.length > 0 else v.toString.length > 0 } } // param("x" -> "y") is notEmpty // param("list" -> Seq(1,2,3)) is notEmpty object notEmpty extends notEmpty(true) case class notEmpty(trim: Boolean = true) extends ValidationRule { def name = "notEmpty" def isValid(v: Any) = !isEmpty(v) && { toHasSize(v).map(x => x.size > 0).getOrElse { if (trim) v.toString.trim.length > 0 else v.toString.length > 0 } } protected def isEmpty(v: Any): Boolean = v == null || v == "" }
诸如 notNull
、 required
与 notEmpty
之类的object或case class都继承自 ValidationRule
,但它们本质上都是函数,接受的参数为 KeyValueParamDefinition
类型,返回 ValidationState
。在这些方法调用的背后,隐含地使用到了Scala的特殊语法:
param("x" -> "y") is notEmpty
param("x" -> "y")
是 ParamDefinition
类型,然后利用隐式转换的方式,使其拥有了 is
方法。因此前面的调用相当于:
param("x" -> "y").is(notEmpty)
该方法接收的参数表面上是 ValidationRule
类型,实则是一个函数,函数参数类型为 KeyValueParamDefinition
,它是 ParamDefinition
的实现类。 param
方法在返回 KeyValueParamDefinition
时,对应的实现是将传入的key/value键值对传递给了 KeyValueParamDefinition
:
def param(kv: (String, Any)): KeyValueParamDefinition = KeyValueParamDefinition(kv._1, kv._2)
因此,就相当于把 (x, y)
这个键值对传递给了 notEmpty()
方法。由于scala的方法默认是strict方法,所以在将 notEmpty
函数传递给 is
方法时,就会去执行 notEmpty
的 apply()
方法,内部就是调用它的 isValid()
方法,进而调用 isEmpty(v: Any)
方法,并将传递进来key/value键值对的value(即这里的y值)进行非空判断。
以上所述就是小编给大家介绍的《Scala实现DSL的框架案例》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 从零快速搭建Next框架案例分享
- Hadoop 框架:MapReduce 基本原理和入门案例
- Android 框架问题分析案例:谁杀了桌面?
- Winform开发框架中的综合案例Demo
- 2 个案例带你迅速入门 Python Flask 框架
- 【案例分享】在 React 框架中使用 SpreadJS 纯前端表格控件
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。