1.引言
静态工厂和构造器有个共同的局限性:它们都不能很好地扩展到大量的可选参数。当初始化类需要较少的参数是,Java 可以尝试实现多种构造方法,scala 相同,可以重载多种apply方法,但是当参数过多时,维护多个构造器将变得困难且难以理解。例如一个User用户类包含10-20个属性,如果我们初始化一个用户类 new User("abc",‘a","b","c",...,"z"),很容易混淆参数,这时候构建器 Builder 就派上用场。
2.构建器Demo
其实在日常程序编写时,经常用到构建器,SparkConf 就是其中之一。初始化spark的过程是先初始化SparkConf类,然后进行各项参数的配置,然后初始化SparkContext类,将SparkConf传入sparkContext 生成 spark。这里 SparkConf 类的作用就是 Builder,SparkContext就是具体要构建的类。
val conf = new SparkConf().setAppName("test").setMaster("local[2]").set("spark.hadoop.validateOutputSpecs", "false")
val sc = new SparkContext(conf)
3.Java实现
User类
用户类只写了两个属性,实际情况可能有数十种属性。在用户类内定义静态方法 Builder 负责创建构建器,所有的默认参数都放在 Builder 类内,最后 Builder 类里要内嵌一个 build 方法,负责生成我们要的用户类。注意到 Builder 类内设置方法的参数都会返回 this 即自己本身,所以可以链式的调用自己,从而可以像 Spark 那样一直在后面追加参数。
public class User {
private String name;
private Integer age;
private User(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return this.name;
}
// 创建builder的静态方法
public static Builder builder() {
return new Builder();
}
// 通过builder构造用户
public static class Builder {
private String name;
private Integer age;
public Builder setName(String name) {
this.name = name;
return this;
}
public Builder setAge(Integer age) {
this.age = age;
return this;
}
public User build() {
return new User(name, age);
}
}
}
Main类
public class Main {
public static void main(String[] args) {
User.Builder builder = User.builder().setName("test").setAge(25);
User user = builder.build();
System.out.println("name: " + user.getName());
}
}
通过 getName 方法得到 Builder 内设置的参数。
4.Scala实现
scala实现与java大同小异,这里userBuilder类的身份就像是 sparkConf,User类就像是 SparkContext,最终获取用户类。
class User(builder: userBuilder) {
val name = builder.name
val mode = builder.mode
}
class userBuilder {
var name: String = _
var mode: String = _
def setName(name: String): userBuilder = {
this.name = name
this
}
def setMode(mode: String): userBuilder = {
this.mode = mode
this
}
def build(): User = {
new User(this)
}
}
object Robot {
def main(args: Array[String]): Unit = {
// 类似于用sparkConf构造spark
val User = new userBuilder()
.setName("abc")
.setMode("test")
.build()
println(User.name)
}
}
5.回看
这里再回看上面 Spark 的方法和上述实现的方法,有相同之处也有不同之处,相同之处是都使用了链式构造参数方法。SpackConf 代码内多个set方法都基于下述set方法开发,可以看到最后返回this,和上面样例相似。不同之处是初始化 SparkConf 并没有用到 build 方法,这是因为 SparkContext 内置了 this 方法,会根据 SparkConf 构造本身,这里只列出最基础的 SparkContext 构造方法,源码内还有多种 this 构造方法,类似于实现多种 apply 方法,有兴趣可以看看。
SparkConf
private[spark] def set(key: String, value: String, silent: Boolean): SparkConf = {
if (key == null) {
throw new NullPointerException("null key")
}
if (value == null) {
throw new NullPointerException("null value for " + key)
}
if (!silent) {
logDeprecationWarning(key)
}
settings.put(key, value)
this
}
SparkContext
def this() = this(new SparkConf())