Builder模式常用例子
今天就简单粗暴的介绍一下Builder模式概念和使用。
- Dialog的创建
AlertDialog dialog = new AlertDialog.Builder(this)
.setIcon(R.mipmap.ic_launcher)
.setMessage("Hello World")
.setTitle("Dialog")
.create();
dialog.show();
- OkHttp Request的创建
Request request = new Request.Builder()
.url("https://github.com/hongyangAndroid")
.build();
Builder模式的好处/优点
客户端代码的可用性和可读性得到了大大提高。与此同时,构造函数的参数数量明显减少调用起来非常直观。【不然创建dialog构造函数时new AlertDialog(R.mipmap.ic_launcher,"Hello World","Dialog","How are you")
,参数数量多的话客户端感觉就是黑人问号】,在提高代码可读性的同时,使用IDE提供的代码补全功能也更加容易。
Builder例子
下面举个关于麦当劳点餐的栗子
public class McFood {
private int totalCount; //份数
private boolean addIce; //饮料是否去冰
private Hamburg hamburg; //汉堡包
private Drink drink; //饮品
private String remark; //备注
private boolean takeOut; //是否外带
public static class Hamburg {//汉堡包类
public static final String HAMBURG_CHICKEN = "chicken";
public static final String HAMBURG_BEEF = "beef";
public Hamburg(String hamburgName) {
this.hamburgName = hamburgName;
}
public String getHamburgName() {
return hamburgName;
}
private String hamburgName;
}
public static class Drink {//饮品类
public static final String DRINK_COLA = "cola";
public static final String DRINK_SPRITE = "sprite";
public Drink(String drinkName) {
this.drinkName = drinkName;
}
private String drinkName;
public String getDrinkName() {
return drinkName;
}
}
}
引入Builder类,增强客户端可用性和可读性【McFood的内部类】
public static class Builder {
private int totalCount = 0;
private boolean addIce = false;
private Hamburg hamburg = null;
private Drink drink = null;
private String remark = null;
private boolean takeOut = false;
public Builder totalCount(int totalCount) {
this.totalCount = totalCount;
return this;
}
public Builder addIce(boolean addIce) {
this.addIce = addIce;
return this;
}
public Builder hamburg(Hamburg hamburg) {
this.hamburg = hamburg;
return this;
}
public Builder drink(Drink drink) {
this.drink = drink;
return this;
}
public Builder remark(String remark) {
this.remark = remark;
return this;
}
public Builder takeOut(boolean takeOut) {
this.takeOut = takeOut;
return this;
}
public McFood create() { // 构建,返回一个新对象
return new McFood(this);
}
}
当然还要设置McFood的构造方法【引入Builder】
public McFood(Builder builder) {
this.totalCount = builder.totalCount;
this.addIce = builder.addIce;
this.hamburg = builder.hamburg;
this.drink = builder.drink;
this.remark = builder.remark;
this.takeOut = builder.takeOut;
}
最后我们来看一下客户端的操作方式
McFood food = new McFood.Builder()
.drink(new Drink(DRINK_COLA))
.addIce(false) //去冰
.hamburg(new Hamburg(HAMBURG_BEEF)) //牛肉汉堡
.takeOut(true) //外带
.totalCount(3) //总共*3
.create();
看完代码高下立判,调用传统JAVA构造器的客户端代码的可读性远不如使用了Builder的客户端代码。同一类型的变量和空放置在一起被调用将会导致一些微妙的错误。(试想,如果客户端不小心颠倒了其中的几个参数顺序,编译不会出错但在运行时肯定出错)【如果没用builder模式,构造方法把去冰/外带 顺序写反了,那就GG了】
Builder模式的缺点
使用Builder模式是肯定会增加代码量的。此外,尽管客户端的代码可读性明显改善,但随之而来的客户端代码变得更加冗长。【但是前者更有价值】
Builder会增加个类代码,这也意味着开发者在给类增加属性时有时会忘记给该属性添加支持的builder。为了克服这个问题,通常将builder嵌套到类中,这样可以很容易地发现哪个相关的builder需要更新。尽管忘记的风险依旧存在,但是这风险就像忘记给类的新属性增加 toString()、 equals(Object)、 hashCode()或其他类基于是所有属性的方法一样。
总结
构建对象时,如果碰到类有很多参数——其中很多参数类型相同而且很多参数可以为空时,推荐Builder模式来完成。当参数数量不多、类型不同而且都是必须出现时,通过增加代码实现Builder往往无法体现它的优势。在这种情况下,理想的方法是调用传统的构造函数。再者,如果不需要保持不变,那么就使用无参构造函数调用相应的set方法吧。