BUILDER生成器——对象创建型模式

Builder模式是一种一步步创建一个复杂对象的设计模式,将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。主要在以下两个场景会使用Builder模式:

  1. 构建复杂对象,屏蔽内部构建细节。
  2. 解耦对象的构建过程和具体表示。

BUILDER生成器——对象创建型模式_第1张图片

场景一:构建复杂对象,屏蔽内部构建细节

当一个类的成员变量很多,且可能有些成员变量不需要在对象初始化时初始化,那为了满足不同场景灵活地构造对象,就需要有很多重载的构造函数。这一方面会给用户造成困扰,不知道该选择哪一个构造函数;另一方面当构造函数中参数很多时,很容易发生变量错位的错误。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类图如下:

角色介绍:

  • Product : 产品的抽象类,具体的构建器可生成不同的产品子类;
  • Builder: 抽象类,规范产品的组建,而由子类实现具体的组建过程;
  • ConcreteBuilder : 具体的构建器;
  • Director : 统一产品的组装过程(可省略)。

下面以汽车组装为例进行说明:

  1. 产品类:

     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;
     }
    
  2. 抽象Builder类

组装汽车有一套汽车组装的标准规范模版,这里简化为只提供了设置车轮和引擎的方法,以及生成汽车实例的build方法:

    public abstract class Builder {
        public abstract void buildWheels (String wheels);
        public abstract void buildEngine (String engine);
        public abstract Car build ();
}
  1. 具体的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;
         }
     }
    
  2. 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();
         }
     }
    
  3. 客户调用指挥者类组装汽车:

最后客户只需要调用指挥者类组装汽车,只需要提供我们想要轮子和引擎即可,至于怎样组装的汽车的细节无需知道,并且可以替换不同的构建者子类来组装不同的汽车。

    public class CreateCar {
        public static void main(String[] args){
            Builder builder = new BenzBuilder ();
            Director direcror=new Director (builder);
            // 组装汽车
            direcror.buildCar ("xx", "yy");
        }
    }

你可能感兴趣的:(设计模式)