【发布时间】:2016-02-23 14:09:11
【问题描述】:
在 Scala 中具有以下构建器模式。为了简化它,我使用了A 的3 个实例,这样instance1 只包含field1,并且与field2 或field3 没有连接。问题是在代码中的任何地方我都必须使用val s = A.instance1.field1.get; doSomething(s),而get 调用可能并不安全。例如A.instance1.field2.get 将在None.get 上失败。为了保护它,我必须将 case 与选项匹配并处理 None 情况:
object A {
val instance1 = new ABuilder().withField1("abc").build1
val instance2 = new ABuilder().withField1("abc").withField2("def").build2
val instance3 = new ABuilder().withField1("abc").withField3("def").build1
}
case class A(builder: ABuilder) {
val field1: Option[String] = builder.field1
val field2: Option[String] = builder.field2
val field3: Option[String] = builder.field3
}
class ABuilder {
var field1: Option[String] = None
var field2: Option[String] = None
var field3: Option[String] = None
def withField1(f: String): ABuilder = {
this.field1 = Some(f)
this
}
def withField2(f: String): ABuilder = {
this.field2 = Some(f)
this
}
def withField3(f: String): ABuilder = {
this.field3 = Some(f)
this
}
def build1: A = {
require(field1.isDefined, "field 1 must not be None")
A(this)
}
def build2: A = {
require(field1.isDefined, "field 1 must not be None")
require(field2.isDefined, "field 2 must not be None")
A(this)
}
}
另一种解决方案是使用参数化类型,也称为幻像类型。我发现很少有关于该主题的好教程,并且在其中找不到如何在 Scala 中使用幻像类型和实际数据(或状态)实现类型安全的构建器模式 - 所有示例仅描述方法。
如何在我的示例中使用幻像类型来避免出现运行时 None 异常并仅获得不错的类型不匹配异常?我正在尝试参数化所有提到的类和方法并使用密封特征,但到目前为止没有成功。
【问题讨论】:
-
field2或field3可以有默认值吗?
-
不,我试图不使用
.getOrElse,而是使用两种不同的构建器子类型。这个例子是关于幻像类型,或参数化类型及其边界。 -
您不需要幻像类型。只需让您的构建器在构造函数中接受所需的参数:
new ABuilder("foo").withField2("bar") -
另外,如果您希望
field1始终存在,并且不希望在任何地方使用额外的.get污染您的代码(可以理解),只需将其声明为String而不是比Option[String]。而且,将您的A声明为这样的案例类似乎令人困惑。要么让它成为一个普通的类,要么把建筑移到一个伴生对象中。 -
@HordonFreeman 不管是字符串还是非字符串,相同的逻辑适用于任何类型。您没有必须使用选项。事实上,您不应该对必填字段使用选项。这很麻烦、令人困惑,并且违背了
Option类型的预期目的。
标签: scala types scala-2.10 builder-pattern