引言
在Android开发中,你或许看到过以下初始化对象的方法.
比如新建Retrofit
对象的时候:
Retrofit.Builder()
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(OkHttpClient())
.baseUrl("https://juejin.cn")
.build()
又比如你新建一个AlertDialog
的时候:
AlertDialog.Builder(this)
.setMessage("hello juejin")
.setView(View(this))
.setIcon(R.drawable.ic_dialog)
.create()
这种写法让我着迷的地方在于它的链式调用,给人一种写代码就像写诗一样的感觉,而且这种写法对对象属性的初始化也不分顺序,只要在调用builder()
之前依次调用即可,很是舒服。
而这种写法就是使用的设计模式中的建造者模式,也称Builder,模式.
优势以及实现
在未使用建造者模式之前,我们是怎么初始化对象并赋值属性的呢?举个栗子,对于一个人,我们假设他有这些属性:
public class Person {
private int name;//姓名属性
private int age; //年龄属性
private String gender;//性别属性
private float height; //身高属性
private float weight; //体重属性
private String idCard;//身份证号码
.....
省略的`getter`和`setter`方法
}
如果我们需要Person
在被实例化的时候属性得到赋值,我们可能会这样做:
public class Person {
private int name;//姓名属性
private int age; //年龄属性
private String gender;//性别属性
private float height; //身高属性
private float weight; //体重属性
private String idCard;//
//仅仅在构造函数中赋值 姓名和年龄属性
public Person(int name, int age) {
this.name = name;
this.age = age;
}
//在构造函数中赋值 姓名,年龄,和性别属性
public Person(int name, int age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
//在构造函数中赋值 姓名,年龄,性别,身高,体重属性
public Person(int name, int age, String gender, float height, float weight) {
this.name = name;
this.age = age;
this.gender = gender;
this.height = height;
this.weight = weight;
}
//在构造函数中赋值 姓名,年龄,性别,身高,体重,身份证属性
public Person(int name, int age, String gender, float height, float weight, String idCard) {
this.name = name;
this.age = age;
this.gender = gender;
this.height = height;
this.weight = weight;
this.idCard = idCard;
}
.....
省略的`getter`和`setter`方法
}
我们利用不同构造函数的多个重载达到不同程度的属性赋值效果,这种方法我们称之为telescoping constructor(重叠构造器)
模式.对于这种模式,固然可以达到效果,但是无论是从代码的编写和可读上面来看,都是很糟糕的,因此,对于这种属性繁多,又需要在初始化的时候赋值的情况,我们便可以使用Builder
模式.试着来改造上面的栗子.
public class Person {
private String name;//姓名属性
private int age; //年龄属性
private String gender;//性别属性
private float height; //身高属性
private float weight; //体重属性
private String idCard;//身份证号码
//需要通过builder进行构建的构造函数,在构造函数中,将builder中的属性一一赋值给person
private Person(Builder builder) {
name = builder.name;
age = builder.age;
gender = builder.gender;
height = builder.height;
weight = builder.weight;
idCard = builder.idCard;
}
//不需要通过builder进行构建的构造函数,
public Person() {
}
//采用静态内部类的原因是静态内部类可独立于外部类进行实例
static class Builder {
private String name;//姓名属性
private int age; //年龄属性
private String gender;//性别属性
private float height; //身高属性
private float weight; //体重属性
private String idCard;//身份证号码
//build()方法,在属性设置完毕后调用,在内部实例person,并传入自身
public Person build() {
return new Person(this);
}
//set方法,注意,设置值之后返回自身,才能达成链式调用
public Builder setName(String name) {
this.name = name;
return this;
}
public Builder setAge(int age) {
this.age = age;
return this;
}
public Builder setGender(String gender) {
this.gender = gender;
return this;
}
public Builder setHeight(float height) {
this.height = height;
return this;
}
public Builder setWeight(float weight) {
this.weight = weight;
return this;
}
public Builder setIdCard(String idCard) {
this.idCard = idCard;
return this;
}
}
//toString()方法,方便打印
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Person:")
.append("\n")
.append("name:")
.append(name)
.append("\n")
.append("age:")
.append(age)
.append("\n")
.append("gender:")
.append(gender)
.append("\n")
.append("height:")
.append(height)
.append("\n")
.append("weight:")
.append(weight)
.append("\n")
.append("idCard:")
.append(idCard);
return sb.toString();
}
.....
省略的`getter`和`setter`方法
}
上面的代码就已经改造成功啦。总结下改造的方式:
- 在需要使用
Builder
模式的类中,建立静态内部类Builder
. - 增加带一个参数类型为自身内部
Builder
的构造函数,并将Builder
中所有属性赋值给自己. Builder
中有外部内全部属性,并有对应set
方法,set
方法的返回值为自身.Builder
中,builder()
负责实例化外部类,并传入自身。
测试代码:
public static void main(String[] args) {
Person person = new Person.Builder()
.setName("cs丶")
.setAge(18)
.setGender("Man")
.setHeight(185)
.setWeight(68)
.setIdCard("10086")
.build();
System.out.println(person);
}
打印的结果:
Person:
name:cs丶
age:18
gender:Manheight:185.0
weight:68.0
idCard:10086
总结
Builder
模式虽好,可不要贪杯哦,对于属性较少的类,还是老老实实用构造函数初始化就行了。