Builder模式

引言

在Java编程中,常常需要为一个Bean构建成员变量或者构建参数,常用的方法有使用构造函数、使用JavaBean的set()方法,但是这两个方案或多或少都存在一定的缺点,于是今天的主角builder模式出场了,它解决了这种典型应用场景的问题,采用简洁明了的使用方式,灵活多变的链式调用,使得多个参数的Bean的构建变得十分简洁。

本文从实际例子来看builder模式到底是什么?如何使用?

以下使用一个Student Bean来进行举例说明。

一、传统的构造函数

传统方式都是使用构造函数在构建Bean,例如存在一个学生类Student,包含id,name,age,height几个成员变量,其中idname是必须的,ageheight是可选的。如果使用传统的构造函数,一般情况下编码如下

public class Student {

   private final String name;

   private final int id;

   private final int age;

   private final int height;

   public Student(String name,int id,int age, int height){

       this.name = name;

       this.id = id;

       this.age = age;

       this.height = height;

   }

   public Student(String name,int id) {

       this(name,id,0,0);

   }

   public Student(String name,int id,int age){

       this(name,id,age,0);

   }

    

   public String getName() {

       return name;

   }

   public int getId() {

       return id;

   }

   public int getAge() {

       return age;

   }

   public int getHeight() {

       return height;

   }

   @Override

   public String toString() {

       return "Student{" +

               "name='" + name + '\'' +

               ", id=" + id +

               ", age=" + age +

               ", height=" + height +

               '}';

   }

    

}

存在的问题主要有两个

  1. 构造函数众多,显得十分繁琐,如果参数继续增加,则构造函数会更多
  2. 无法很好地区分参数,例如public Student(String name,int id,int age),可选地参数是age,但是从构造函数上很难看出来,并且由于height与age一样都是int类型,所以不能出现public Student(String name,int id,int age),也就是没办法实现只传入一个age或者一个height参数地情况,而是不得不使用public Student(String name,int id,int age, int height),即便我只是想设置一个height不想设置age,我也必须为这个age字段设置一个默认值。

二、JavaBean setter方法

为了解决以上问题,又一个经典地方案出现了,这就是JavaBean的setter方法,需要构造一个参数为空的构造函数,所有的成员变量通过setter方法进行设置。

public class Student {

   private  String name;

   private  int id;

   private  int age;

   private  int height;

   public Student(){

   }

   public void setName(String name) {

       this.name = name;

   }

   public void setId(int id) {

       this.id = id;

   }

   public void setAge(int age) {

       this.age = age;

   }

   public void setHeight(int height) {

       this.height = height;

   }

   public String getName() {

       return name;

   }

   public int getId() {

       return id;

   }

   public int getAge() {

       return age;

   }

   public int getHeight() {

       return height;

   }

   @Override

   public String toString() {

       return "Student{" +

               "name='" + name + '\'' +

               ", id=" + id +

               ", age=" + age +

               ", height=" + height +

               '}';

   }

}

这个方法也有比较明显的缺点,首先参数多的时候就需要调用很长的一系列setter方法,另外无法区分哪些是必须设置的成员变量,哪些是可选的成员变量,并且成员变量多的时候很容易漏掉一些setter,并且难以检查出来,很容易造成错误。

三、Builder模式

使用Builder模式使得这一切变得更加简洁,方便使用,先来看看Builder模式下如何编码

package tech.liujintao.leetcode;

public class Student {

   private final String name;

   private final int id;

   private final int age;

   private final int height;

   private Student(StudentBuilder studentBuilder) {

       this.name=studentBuilder.name;

       this.id =studentBuilder.id;

       this.age = studentBuilder.age;

       this.height = studentBuilder.height;

   }

   public static class StudentBuilder

   {

       private String name;

       private int id;

       private int age;

       private int height;

       public StudentBuilder(String name,int id)

       {

           this.name = name;

           this.id = id;

       }

       public StudentBuilder age(int age)

       {

           this.age=age;

           return this;

       }

       public StudentBuilder height(int height)

       {

           this.height = height;

           return this;

       }

       public Student build()

       {

           return new Student(this);

       }

   }

   public String getName() {

       return name;

   }

   public int getId() {

       return id;

   }

   public int getAge() {

       return age;

   }

   public int getHeight() {

       return height;

   }

   @Override

   public String toString() {

       return "Student{" +

               "name='" + name + '\'' +

               ", id=" + id +

               ", age=" + age +

               ", height=" + height +

               '}';

   }

   public static void main(String[] args) {

       Student student = new StudentBuilder("codingway",111).age(18).height(2).build();

       System.out.println(student.toString());

   }

}

首先将Student类中的成员变量都声明为private final,并且将构造函数也声明为private,这样子就保证了无法通过Student的构造函数在构建Student Bean,并且成员变量也无法被修改,对外只提供getter方法用于或者成员变量。

随后构造一个StudentBuilder类,StudentBuilder类是一个静态类 ,其构造函数可以设置为Student必须的属性,例如id和name,其他可选的变量放到方法中,例如age和height,每个方法返回StudentBuilder本身。

Student对象只有一个构造函数,其参数就是StudentBuilder,于是所有成员变量地设置由StudentBuilder接管了,而StudentBuilder控制了哪些成员变量必须赋值,哪些是可选的,最后通过build方法构造Student Bean。使用者一目了然,链式调用更加清晰。

这就是Builder模式被大家所喜爱,并且在很多著名的开源项目中被采用的原因,特别是在一些需要配置环境参数并且参数众多的场景下。

上一篇:Gitclient使用


下一篇:PermGen OutOfMemory问题排查