Builder.build()是否应返回默认状态?

时间:2019-05-31 13:08:07

标签: design-patterns builder builder-pattern

使用Builder模式时,总是存在一个问题,即这些字段是否具有默认值?我找不到明确定义的可靠来源...

问题是可读性:Builder返回什么?我始终需要检查build()的特定实现,以查看使用了哪些默认值。不应将Builder用于创建定义上没有简单默认状态的复杂对象吗?

一种替代方法是检查是否在fun build() : Car { return if (doors != null && hp != null) Car(doors, hp, color) // color can be null else throw IllegalArgumentException("Door count and HP is mandatory!") } 方法内设置了所有必填字段:

v1 = c("2000-05-01", "2000-05-02", "2000-05-03", "2000-05-04", "2000-05-05")
v2 = seq(2,20, length = 5)
v3 = seq(-2,7, length = 5)
v4 = seq(-6,3, length = 5)

df1 = data.frame(Date = v1, df1_Tmax = v2, df1_Tmean = v3, df1_Tmin = v4)
dfl1 <- list(df1, df1, df1, df1)
names(dfl1) = c("ABC_1", "DEF_1", "GHI_1", "JKL_1")

v1 = c("2000-05-01", "2000-05-02", "2000-05-03", "2000-05-04", "2000-05-05")
v2 = seq(3,21, length = 5)
v3 = seq(-3,8, length = 5)
v4 = seq(-7,4, length = 5)

df2 = data.frame(Date = v1, df2_Tmax = v2, df2_Tmean = v3, df2_Tmin = v4)
dfl2 <- list(df2, df2, df2, df2)
names(dfl2) = c("ABC_2", "DEF_2", "GHI_2", "JKL_2")

v1 = c("2000-05-01", "2000-05-02", "2000-05-03", "2000-05-04", "2000-05-05")
v2 = seq(4,22, length = 5)
v3 = seq(-4,9, length = 5)
v4 = seq(-8,5, length = 5)

df3 = data.frame(Date = v1, df3_Tmax = v2, df3_Tmean = v3, df3_Tmin = v4)
dfl3 <- list(df3, df3, df3, df3)
names(dfl3) = c("ABC_3", "DEF_3", "GHI_3", "JKL_3")

v1 = c("2000-05-01", "2000-05-02", "2000-05-03", "2000-05-04", "2000-05-05")
v2 = seq(2,20, length = 5)
v3 = seq(-2,8, length = 5)
v4 = seq(-6,3, length = 5)

abc = data.frame(Date = v1, ABC_Tmax = v2, ABC_Tmean = v3, ABC_Tmin = v4)

abclist <-list(abc, abc, abc, abc)

names(abclist) = c("ABC_abc", "DEF_abc", "GHI_abc", "JKL_abc")

...或者这被认为是不好的做法?

2 个答案:

答案 0 :(得分:1)

我不会将默认值放到Car类中,而是将默认值放到具体的构建器中。

class SedanBuilder {
    var doors = 4
    var wheels = 4
    var driver = null // we have to set driver

    ... setters ...
    fun build() {

        return Car(wheels, doors, driver)
    }
}

和其他构建器可以使用其他默认值

class SchumachersCarBuilder {
    var doors = 4
    var wheels = 4
    var driver = Person("Michael")

    ... setters ...
    fun build() {

        return Car(wheels, doors, driver)
    }
}

当然,您必须检查Car构造函数中的所有必需参数

答案 1 :(得分:1)

您没有找到问题的一般答案,因为没有答案。这取决于构建器所使用的上下文或构建器正在构建的对象的详细信息。

有时候,最好返回一个默认对象,该对象的所有成员都初始化为默认值。但是,此默认对象必须有效。例如。要构建记录器对象,返回使用默认格式和默认日志级别记录到控制台的记录器是有效的。有人可以立即使用它。

但是有时纯粹的默认对象没有意义。创建完全配置的默认HTTP客户端是不明智的,因为它不会提供最低预期的行为,因为目标URL可能是意外的。在这种情况下,您可以为构建器类编写一个构造函数,该构造函数将URL作为参数(也可能是一些数据对象),然后使用默认值(例如,默认请求标头,默认超时,默认缓冲区大小等)预先配置客户端对象。 。然后,该对象将满足最低使用预期。随后,每次对setter的调用都将覆盖默认值,即在每个setter接受参数之前,应先检查其有效性。

但是您应该始终在可能的情况下尝试使用默认值而非异常。当对象的构造需要强制性信息时,请将其放入构造函数中以使其公开。这样,您可以确保对象始终处于有效和有用的状态。对于HTTP客户端构建器,如果URL格式错误,则可能引发异常,以向开发人员提示其构造URL的代码可能存在缺陷。也许读Best practices for exceptions

考虑到编写构造函数以便收集所有必需参数(不能设置为有用的默认值)的解决方案,您的build() finalize方法可以而且应该始终且在任何时候返回有效且有用的方法宾语。这使您的构建器很方便。 (否则,构建器的用户将被迫阅读文档以知道要调用哪些setter。每个人都知道您不喜欢编写文档。每个人都知道您不喜欢 >在使用某些类之前先阅读文档。每个人都有相同的想法。