引言
在Java編程中,常常需要爲一個Bean構建成員變量或者構建參數,常用的方法有使用構造函數、使用JavaBean的set()方法,但是這兩個方案或多或少都存在一定的缺點,於是今天的主角builder模式出場了,它解決了這種典型應用場景的問題,採用簡潔明瞭的使用方式,靈活多變的鏈式調用,使得多個參數的Bean的構建變得十分簡潔。
本文不想以傳統的類圖的形式來講解,而是從實際例子來看builder模式到底是什麼?如何使用?
以下使用一個Student Bean來進行舉例說明。
一、傳統的構造函數
傳統方式都是使用構造函數在構建Bean,例如存在一個學生類Student,包含id,name,age,height
幾個成員變量,其中id
和name
是必須的,age
和height
是可選的。如果使用傳統的構造函數,一般情況下編碼如下
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 +
'}';
}
}
存在的問題主要有兩個
- 構造函數衆多,顯得十分繁瑣,如果參數繼續增加,則構造函數會更多
- 無法很好地區分參數,例如
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模式被大家所喜愛,並且在很多著名的開源項目中被採用的原因,特別是在一些需要配置環境參數並且參數衆多的場景下。
更多內容
- 博客:http://www.blog.liujintao.tech
- 公衆號: 編程之路