思路是这样,每个表单实现Validatable特质。这个特质要求实现返回一个属性名到验证器列表的映射。
表单验证器执行时首先获取表单所有的属性(除去class),然后遍历这个映射,运行字段对应的验证器变成响应的验证错误(如果有的话),否则最后是个空集合。
FormValidatorRunner(代码最下方)是测试类,执行结果是
Map(name -> List(ValidateError(default.notBlank,List())))
代码如下:
import scala.collection.Traversable case class ValidateError(messageCode: String, messageArguments: Traversable[Any] = List[Any]()) trait FieldRule { def apply(form: Validatable, propertyValue: Option[Any]): Option[ValidateError] } object NotBlankFieldRule extends FieldRule { def apply(form: Validatable, propertyValue: Option[Any]) = propertyValue match { case Some(value) if value.toString.trim.length > 0 => None case _ => Some(ValidateError("default.notBlank")) } } trait Validatable { def getRules(): Map[String, Traversable[FieldRule]] } class FormError(fieldErrors: Map[String, Traversable[ValidateError]]) { def hasError(): Boolean = fieldErrors.isEmpty override def toString = fieldErrors.toString } object FormValidator { def apply(form: Validatable): FormError = { val props = getProps(form) new FormError(form.getRules.map{ case (fieldName, rules) => (fieldName, rules.flatMap(_.apply(form, props.get(fieldName)))) }) } private def getProps(obj: Any): Map[String, Any] = { java.beans.Introspector.getBeanInfo( obj.getClass).getPropertyDescriptors.flatMap{ d => Option(d.getReadMethod).map(getter => (d.getName, getter.invoke(obj))) }.toMap - "class" } } object FormValidatorRunner { class Person(id: Long, name: String) extends Validatable { def getId(): Long = id def getName(): String = name def getRules = Map("name" -> List(NotBlankFieldRule)) } def main(args: Array[String]): Unit = { println(FormValidator.apply(new Person(1L, ""))) } }