内容简介:在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 纯前端表格控件
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Head First Rails
David Griffiths / O'Reilly Media / 2008-12-30 / USD 49.99
Figure its about time that you hop on the Ruby on Rails bandwagon? You've heard that it'll increase your productivity exponentially, and allow you to created full fledged web applications with minimal......一起来看看 《Head First Rails》 这本书的介绍吧!