设计模式之Builder模式(链式调用)
问题背景:Builder模式在很多地方都有用到,代码风格也比较简洁,但是对其深入的了解并不多,因此在参考其他博客的基础上写此文章。首先先看我的实际应用到Builder模式的场景:场景一:这是在编写RPC简易框架时,需要输入服务端的配置信息时RPC.Server server = new RPC.Builder(new Configuration()).setBindAddress("localho
问题背景:
Builder模式在很多地方都有用到,代码风格也比较简洁,但是对其深入的了解并不多,因此在参考其他博客的基础上写此文章。
首先先看我的实际应用到Builder模式的场景:
场景一:
这是在编写RPC简易框架时,需要输入服务端的配置信息时
RPC.Server server = new RPC.Builder(new Configuration())
.setBindAddress("localhost")
.setPort(8888)
.setProtocol(RPCProtocol.class)
.setInstance(new NNServer())
.build();
场景二:
此处是编写ES的索引时
//创建插入类 Index Builder中的参数表示要插入到索引中的文档,底层会转换Json格式的字符串,所以也可以将文档封装为样例类对象
val index: Index = new Index.Builder(source)
.index("movie_index_5")
.`type`("movie")
.id("1")
.build()
问题分析
在上述两个场景中,我们可以看到Builder模式的应用,都是在初始化有多个参数的情境下,那么为什么要用Builder解决问题呢,他有什么优势,我们以一个例子讲解:
public class User {
private final String firstName; // 必传参数
private final String lastName; // 必传参数
private final int age; // 可选参数
private final String phone; // 可选参数
private final String address; // 可选参数
}
在这个类中,有些参数是必要的,而有些参数是非必要的。那么问题就来了,如何创建这个类的对象呢?
方法一(使用构造方法)
第一个构造方法只包含两个必需的参数,第二个构造方法中,增加一个可选参数,第三个构造方法中再增加一个可选参数,依次类推,直到构造方法中包含了所有的参数。
public User(String firstName, String lastName) {
this(firstName, lastName, 0);
}
public User(String firstName, String lastName, int age) {
this(firstName, lastName, age, "");
}
public User(String firstName, String lastName, int age, String phone) {
this(firstName, lastName, age, phone, "");
}
public User(String firstName, String lastName, int age, String phone, String address) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
this.phone = phone;
this.address = address;
}
这样虽然可以运行,但是有以下缺点:
①参数多时将很麻烦
②对于只想传一个参数,且参数位置在后边,如传入address,那么只能采用最后一个构造方法,且需对前几个参数赋值,这是耗时且不必要的操作。
方法二(采用get,set方式)
我们同样可以根据JavaBean的习惯,设置一个空参数的构造方法,然后为每一个属性设置setters和getters方法
public class User {
private String firstName; // 必传参数
private String lastName; // 必传参数
private int age; // 可选参数
private String phone; // 可选参数
private String address; // 可选参数
public User() {
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public int getAge() {
return age;
}
public String getPhone() {
return phone;
}
public String getAddress() {
return address;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public void setAge(int age) {
this.age = age;
}
public void setPhone(String phone) {
this.phone = phone;
}
public void setAddress(String address) {
this.address = address;
}
}
这种方法看起来可读性不错,而且易于维护。作为调用者,创建一个空的对象,然后只需传入所需要的参数,但是仍有以下缺点:
①对象会产生不一致的状态。当你想要传入5个参数的时候,你必需将所有的setXX方法调用完成之后才行。然而一部分的调用者看到了这个对象后,以为这个对象已经创建完毕,就直接使用了,其实User对象并没有创建完成。
②类是可变的了,不可变类所有好处都不复存在。
此时,Builder模式便可以解决以上问题。
public class User {
private final String firstName; // 必传参数
private final String lastName; // 必传参数
private final int age; // 可选参数
private final String phone; // 可选参数
private final String address; // 可选参数
private User(UserBuilder builder) {
this.firstName = builder.firstName;
this.lastName = builder.lastName;
this.age = builder.age;
this.phone = builder.phone;
this.address = builder.address;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public int getAge() {
return age;
}
public String getPhone() {
return phone;
}
public String getAddress() {
return address;
}
public static class UserBuilder {
private final String firstName;
private final String lastName;
private int age;
private String phone;
private String address;
public UserBuilder(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public UserBuilder age(int age) {
this.age = age;
return this;
}
public UserBuilder phone(String phone) {
this.phone = phone;
return this;
}
public UserBuilder address(String address) {
this.address = address;
return this;
}
public User build() {
return new User(this);
}
}
}
这种方式有以下优点:
①类的构造方法是私有的。也就是说调用者不能直接创建User对象。
②类的属性都是不可变的。所有的属性都添加了final修饰符,并且在构造方法中设置了值。并且,对外只提供getters方法。
③Builder的内部类构造方法中只接收必传的参数,并且该必传的参数适用了final修饰符。
因此,创建一个User类对象的代码为:
new User.UserBuilder("王", "小二")
.age(20)
.phone("123456789")
.address("亚特兰蒂斯大陆")
.build();
代码分析:
UserBuilder类是内部类,且被static修饰,则为静态内部类。它有以下特点:
1.静态内部类跟静态方法一样,只能访问静态的成员变量和方法,不能访问非静态的方法和属性,但是普通内部类可以访问任意外部类的成员变量和方法
2.静态内部类可以声明普通成员变量和方法,而普通内部类不能声明static成员变量和方法。
3.静态内部类可以单独初始化
4.外部类访问成员内部类的成员,需要“内部类.成员”或“内部类对象.成员”的方式
静态内部类使用场景一般是当外部类需要使用内部类,而内部类无需外部类资源,并且内部类可以单独创建的时候会考虑采用静态内部类的设计。
总结:
1.如果类的构造器或静态工厂中有多个参数,设计这样类时,最好使用Builder模式,特别是当大多数参数都是可选的时候。
2.如果现在不能确定参数的个数,最好一开始就使用构建器即Builder模式。
参考博客:
https://www.jianshu.com/p/e2a2fe3555b9
https://blog.csdn.net/czh500/article/details/83876425
https://blog.csdn.net/junehappylove/article/details/85236946
更多推荐
所有评论(0)