这几天在看顾浩鑫的《Android高级进阶》,觉得这本书写得挺不错的,作为进阶一类,设计的知识面很多。在看的时候有一个知识点讲得很好,是关于Builder模式的,之前在学习设计模式的时候通常都是分析UML类图,学一些简单的接口和实现等,关于应用方面学习得比较少,看这本书的时候,刚好有一个章节专门对Builder模式进行讲解,以及它的变种在开发中的应用。学习完之后觉得受益颇多,故将此知识点记录下来。
这个章节最重要的内容是builder模式在javaBean中的应用。这里以一个User对象为例:
public class User {
private String mName;
private int mAge;
private String mPhone;
}
通常我都是这么写的,包括我早期在学习java的时候,教学也是这样讲的,凸显面向对象的封装性,然后给各个属性设置setter和getter方法,这种方法虽然可行,但却有着以下弊端:
①setter方法可能会被过度使用,尤其是在团队开发的时候,一个javaBean可能会经过几个程序员的代码中,设置了setter方法意味着哪个程序员都可以任意修改里面的属性值,这就容易造成一些混乱和错误,毕竟这些属性值是可变的,意味着对象在初始化的时候一些属性可以不对它进行赋值,等到后面某个环节再设置值,这样一来实例的整个状态也是不连续的,代码也会变得不优雅。
②开发过程中通常会对这样的可变类设置多个构造函数来满足多种属性值的初始化情况,在属性多的时候会变得难以维护;
③User类是可变的,这样一来就会失去不可变对象的很多好处,比如说线程安全问题,不可变对象是线程安全的。线程间的变量共享不需要利用特殊机制来保证同步,因为对象的值无法改变。这样一来可以减少同步机制的开销,二来可以降低并发错误的可能性。
所以可以考虑使用builder模式来构建javaBean对象。
public class User {
private final String mName;
private final int mAge;
private final String mPhone;
public String getmName() {
return mName;
}
public int getmAge() {
return mAge;
}
public String getmPhone() {
return mPhone;
}
private User(UserBuilder builder){
mName=builder.mName;
mAge=builder.mAge;
mPhone=builder.mPhone;
}
public static class UserBuilder{
private String mName;
private int mAge;
private String mPhone;
public UserBuilder(String mName, int mAge) {
this.mName = mName;
this.mAge = mAge;
}
public UserBuilder age(int age){
mAge=age;
return this;
}
public UserBuilder name(String name){
mName=name;
return this;
}
public UserBuilder phone(String phone){
mPhone=phone;
return this;
}
public User build(){
return new User(this);
}
}
}
这段代码是仿照《Android高级进阶》这本书写的,从上面的代码可以看出以下几点:
①User类是不可变的,所有属性值都是final类型并且只在构造函数中设置,只提供getter方法;
②User类的构造函数是私有的,不能直接实例化这个类;
③UserBuilder采用Fluent Interface的style(https://blog.csdn.net/time_hunter/article/details/8011124),这样调用返回的时候还是这个对象,可以继续调用该对象的方法,使得实例是个连续状态;
④User对象的属性值是由UserBuilder对象决定的,因为是建造者,所以builder对象可以是不连续状态,等到最终确定或者需要这个User对象的时候再调用build()方法构建对象。这个点非常重要,应用很广泛,因为现在举例的User类是比较简单的,只需要一个UserBuilder即可完成构建,但是在一些稍微复杂对象的构建场合,比如butterknife框架中,对于javaFile的构建包括了几个构建器,MethodBuilder,FieldBuilder等,以后我们再抽空分析下butterknife的源码。
创建User对象,使用builder:
public User getUser(){
return new User.UserBuilder("狗娃",2)
.phone("138266")
.build();
}
这样的创建方式是不是很熟悉?在很多开源框架也是用这种方式构建一些javaBean对象的,比如,在OkHttp框架对请求的封装和创建:
final Request request = new Request.Builder()
.url("https://github.com/hongyangAndroid")
.build();
private Request(Builder builder){
this.url = builder.url;
this.method = builder.method ;
this.headers = builder.headers.build() ;
this.body = builder.body ;
this.tag = builder.tag != null ? builder.tag : this;
}
还有android的dialog创建也是这样的方式,这里就不过多展示了。此外,还有一点,如果嫌写builder麻烦,可以在android studio使用InnerBuilder这个插件来自动生成Builder类,再根据需要微调一下就行了。
后记:builder模式应用在javaBean对象也要看场合,不是所有的javaBean对象都适合用builder模式来构建,比如在社交类APP开发中,一个用户对象的信息通常是可变的,比如它的网名,性别,个人签名等等信息,而一些需要认证的信息比如个人姓名等,一旦设置就不允许随便更改的,所以这样的话一个javaBean对象就可能包含着可变和不可变的对象,具体情况需要具体分析。