Builder模式是一种一步步创建一个复杂对象的设计模式,将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。主要在以下两个场景会使用Builder模式:
当一个类的成员变量很多,且可能有些成员变量不需要在对象初始化时初始化,那为了满足不同场景灵活地构造对象,就需要有很多重载的构造函数。这一方面会给用户造成困扰,不知道该选择哪一个构造函数;另一方面当构造函数中参数很多时,很容易发生变量错位的错误。Builder模式可以很清析地表示出设置对象各个参数的过程,因此,Builder模式通常与Composite模式共用。
Spark中SparkSession类就是使用了Builder模式,下面是SparkSession的部分源码。SparkSession典型的使用方式如下:SparkSession.builder().master("local").appName("test").config(conf).getOrCreate()
,通过Builder模式将对象的一步步创建出来,并且不需要知道其为保证对象单例的构建细节。构建对象过程中每个参数的设置都非常清析,且Builer模式的方法都是可以链式调用,代码看起来非常简洁清析。
object SparkSession extends Logging {
/**
* Builder for [[SparkSession]].
*/
class Builder extends Logging {
private[spark] def sparkContext(sparkContext: SparkContext): Builder = synchronized {
userSuppliedContext = Option(sparkContext)
this
}
def appName(name: String): Builder = config("spark.app.name", name)
def config(key: String, value: String): Builder = synchronized {
options += key -> value
this
}
def master(master: String): Builder = config("spark.master", master)
def enableHiveSupport(): Builder = synchronized {
if (hiveClassesArePresent) {
config(CATALOG_IMPLEMENTATION.key, "hive")
} else {
throw new IllegalArgumentException(
"Unable to instantiate SparkSession with Hive support because " +
"Hive classes are not found.")
}
}
def getOrCreate(): SparkSession = synchronized {
assertOnDriver()
// Get the session from current thread's active session.
var session = activeThreadSession.get()
if ((session ne null) && !session.sparkContext.isStopped) {
options.foreach { case (k, v) => session.sessionState.conf.setConfString(k, v) }
if (options.nonEmpty) {
logWarning("Using an existing SparkSession; some configuration may not take effect.")
}
return session
}
// Global synchronization so we will only set the default session once.
SparkSession.synchronized {
// If the current thread does not have an active session, get it from the global session.
session = defaultSession.get()
if ((session ne null) && !session.sparkContext.isStopped) {
options.foreach { case (k, v) => session.sessionState.conf.setConfString(k, v) }
if (options.nonEmpty) {
logWarning("Using an existing SparkSession; some configuration may not take effect.")
}
return session
}
// No active nor global default session. Create a new one.
val sparkContext = userSuppliedContext.getOrElse {
val sparkConf = new SparkConf()
options.foreach { case (k, v) => sparkConf.set(k, v) }
// set a random app name if not given.
if (!sparkConf.contains("spark.app.name")) {
sparkConf.setAppName(java.util.UUID.randomUUID().toString)
}
SparkContext.getOrCreate(sparkConf)
// Do not update `SparkConf` for existing `SparkContext`, as it's shared by all sessions.
}
applyExtensions(
sparkContext.getConf.get(StaticSQLConf.SPARK_SESSION_EXTENSIONS).getOrElse(Seq.empty),
extensions)
session = new SparkSession(sparkContext, None, None, extensions)
options.foreach { case (k, v) => session.initialSessionOptions.put(k, v) }
setDefaultSession(session)
setActiveSession(session)
// Register a successfully instantiated context to the singleton. This should be at the
// end of the class definition so that the singleton is updated only if there is no
// exception in the construction of the instance.
sparkContext.addSparkListener(new SparkListener {
override def onApplicationEnd(applicationEnd: SparkListenerApplicationEnd): Unit = {
defaultSession.set(null)
}
})
}
return session
}
}
}
JavaBean对象的所有成员变量都有对应的get和set方法,可以看作是Builder模式的简化,JavaBean类往往是有一个默认的无参构造函数,创建实例后再一个个设置成员变量。Builder模式的流程是反过来的,先设置参数,最后生成对象实例。这样可以降低忘记设置某个参数的概率,也保证生成的对象已经是一个完整的对象,并且可用于创建不可变对象,如proto的协议的实现方式。
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。典型的UML类图如下:
角色介绍:
下面以汽车组装为例进行说明:
产品类:
public abstract class Car {
String wheels;
String engine;
}
public class Ferrari extends Car {
String wheels;
String engine;
}
public class Benz extends Car {
String wheels;
String engine;
}
抽象Builder类
组装汽车有一套汽车组装的标准规范模版,这里简化为只提供了设置车轮和引擎的方法,以及生成汽车实例的build方法:
public abstract class Builder {
public abstract void buildWheels (String wheels);
public abstract void buildEngine (String engine);
public abstract Car build ();
}
具体的Builder子类,不同的子类用于组装不同的汽车:
public class FerrariBuilder extends Builder {
private Car car = new Ferrari ();
@Override
public void buildWheels (String wheels) {
car.wheels = wheels;
}
@Override
public void buildEngine (String engine) {
car.engine = engine;
}
@Override
public void build () {
return car;
}
}
public class BenzBuilder extends Builder {
private Car car = new Benz ();
@Override
public void buildWheels (String wheels) {
car.wheels = wheels;
}
@Override
public void buildEngine (String engine) {
car.engine = engine;
}
@Override
public void build () {
return car;
}
}
Director指挥者类来统一组装过程:
public class Direcror {
Builder builder = null;
public Direcror (Builder builder){
this.builder = builder;
}
public Car buildCar (String wheels, String engine){
//规范建造流程
this.builder.buildWheels (wheels);
this.builder.buildEngine (engine);
return builder.build();
}
}
客户调用指挥者类组装汽车:
最后客户只需要调用指挥者类组装汽车,只需要提供我们想要轮子和引擎即可,至于怎样组装的汽车的细节无需知道,并且可以替换不同的构建者子类来组装不同的汽车。
public class CreateCar {
public static void main(String[] args){
Builder builder = new BenzBuilder ();
Director direcror=new Director (builder);
// 组装汽车
direcror.buildCar ("xx", "yy");
}
}