假设Car
模型对象(作为case class
),不可变并使用构建器模式创建。 (Joshua Bloch建造者模式)。
其build
方法调用CarValidator
对象,以便允许创建或不创建相关Car
。否则,它会抛出一个IllegalStateException
,其中包含意外字段。
因此,没有人可以在运行时随时创建陈旧或无效Car
,太棒了!
现在假设一个Web表单来创建Car
。 Play的控制器将包含此表单映射:
val carForm = Form( //this is a conceptual sample
mapping(
"brand" -> nonEmptyText,
"description" -> nonEmptyText,
"maxSpeed" -> number
"complexElement" -> number.verifying(........) //redundant validation here
)(Car.apply)(Car.unapply)
)
在此示例中,有一些基本字段,但想象一下需要复杂业务验证的更复杂字段,例如complexeElement
。
我真的觉得我很容易打破干(不要重复自己)。
实际上,无论表单验证带来什么,这都将由我的Car
构建器的验证器提供,因为模型验证是最重要的验证位置,不应该依赖于其他任何内容。
我想象一个在Helper
附近有Controller
类的解决方案,处理构建器使用的同一验证器对象。但是,它强制我获取所有验证方法public
,以便在我的Web表单的任何验证步骤中独立调用(如上面的代码片段)。
在避免破坏DRY的同时保持这种建造者原则的好习惯是什么?
答案 0 :(得分:2)
如果要保留构建器模式,则不应该Form
创建实例。表单应该确保输入的信息的类型正确。表单无法创建最终Car
,因为它不知道构建Car
的规则,构建器会这样做。
所以我会说你让表单把东西放到一个中间对象(一个元组或PossibleCar
案例类)中,并用该对象构建你的Car
(使用构建器)。
还有另一条路线可用,但这意味着你必须创建(可能很复杂的)结构,让你适应不同类型的验证。然后,构建器和表单都可以使用这些验证(在适配器的帮助下)来创建有效的汽车。我不太了解您所处的情况,并建议您采取哪条路线。