1. 简单例子
建造房子可以认为是一个建造者模式的实际场景。建房可以包括,建造地板、墙,房顶等。(每间房子都是这样的一个建造顺序,但细节是有区别的)
2. 定义
将一个复杂的对象的构建和它的表示分离,使得同样的构建过程可以创建不同的表示。 (使用多个简单的对象一步一步构建成一个复杂的对象)
例子:比如肯德基有很多套餐,同样都是由汉堡、饮料、薯条、甜筒组成,但他们的口味,价格都不一样。
3. 角色
- 抽象建造者:给出一个抽象接口,抽象出肯德基套餐各组成部分的生产方法。
- 具体建造者:持有套餐的引用;实现抽象建造者,实现套餐各部分的生产方法,并将具体的套餐返回给肯德基。
- 指导者:持有建造者的引用,指导建造者组合套餐。
- 产品:也就是这个例子中的肯德基套餐。
4. 具体代码实现
4.1 创建抽象建造者接口
/**
* Created by Administrator on 2017/10/22.
*/
public interface Builder {
// 炸薯条
public void createChips();
// 做饮料
public void createDrinks();
// 做汉堡
public void createHamburger();
// 组合套餐
public KFCSetMeal build();
}
4.2 创建产品类
/**
* 套餐包含薯条、饮料、汉堡
*
* Created by Administrator on 2017/10/22.
*/
public class KFCSetMeal {
private Chips chips;
private Drinks drinks;
private Hamburger hamburger;
public Chips getChips() {
return chips;
}
public void setChips(Chips chips) {
this.chips = chips;
}
public Drinks getDrinks() {
return drinks;
}
public void setDrinks(Drinks drinks) {
this.drinks = drinks;
}
public Hamburger getHamburger() {
return hamburger;
}
public void setHamburger(Hamburger hamburger) {
this.hamburger = hamburger;
}
}
4.3 创建具体的建造者类
/**
* 工作套餐
*
* Created by Administrator on 2017/10/22.
*/
public class WorkPackageBuilder implements Builder{
private static final String TAG = WorkPackageBuilder.class.getSimpleName();
KFCSetMeal kfcSetMeal = new KFCSetMeal();
@Override
public void createChips() {
Chips chips = new Chips();
chips.name = "工作餐薯条";
kfcSetMeal.setChips(chips);
}
@Override
public void createDrinks() {
Drinks drinks = new Drinks();
drinks.name = "工作餐饮料";
kfcSetMeal.setDrinks(drinks);
}
@Override
public void createHamburger() {
Hamburger hamburger = new Hamburger();
hamburger.name = "工作餐汉堡";
kfcSetMeal.setHamburger(hamburger);
}
@Override
public KFCSetMeal build() {
Log.e(TAG,kfcSetMeal.getChips().name+"--" + kfcSetMeal.getDrinks().name + "--" + kfcSetMeal.getHamburger().name);
return kfcSetMeal;
}
}
4.4 创建指导者类
/**
* 指导者
* Created by Administrator on 2017/10/22.
*/
public class KFCDirector {
public KFCSetMeal build(Builder builder){
builder.createChips();
builder.createDrinks();
builder.createHamburger();
return builder.build();
}
}
4.5 调用建造者模式创建不同对象
//肯德基的前台,组合工作餐
Builder builder = new WorkPackageBuilder();
KFCDirector kfcDirector = new KFCDirector();
KFCSetMeal kfcSetMeal = kfcDirector.build(builder);
//肯德基的前台,组合普通套餐
Builder normalPackageBuilder = new NormalPackageBuilder();
KFCDirector kfcDirector2 = new KFCDirector();
KFCSetMeal normalKfcSetMeal = kfcDirector.build(normalPackageBuilder);
结果:
10-21 23:05:38.142 1837-1837/com.builderpattern E/WorkPackageBuilder: 工作餐薯条–工作餐饮料–工作餐汉堡
10-21 23:05:38.142 1837-1837/com.builderpattern E/NormalPackageBuilder: 普通套餐薯条–普通套餐饮料–普通套餐汉堡
5. 扩展–链式编程,仿AlertDialog创建者模式
标准的建造者模式,在工作中基本没有使用场景,使用较多的是将要介绍的这种链式创建者模式。
5.1 链式编程原理
精简建造者中的角色,只保留具体建造者和具体产品,将产品中的get和set方法删除,避免调用者能够了解具体的实现逻辑。在具体建造者中创建一个内部类,copy具体产品的参数,在调用set方法时,将具体参数赋值到内部类中,在builder时再将具体参数copy到具体产品中,返回具体产品对象。实现效果如下:
new AlertDialog.Builder(self)
.setTitle("确认")
.setMessage("确定吗?")
.setPositiveButton("是", null)
.setNegativeButton("否", null)
.show();
5.2 具体实现步骤
删除抽象建造者和指导者,只保留具体建造者和具体的产品
- 给具体建造者内部加上和具体产品有完全相同参数的内部类,用来接收参数
- 改造设置参数方法,用内部类实例类接收,不真正添加到产品对象中
- 改造产品类,设置为成员变量设置为private,让外部调用者不能拿到~,添加方法将参数设置到产品中
- 改造build方法,将参数对象中的参数真正转移到产品对象中
/**
* KFC套餐
*
* Created by Administrator on 2017/10/22.
*/
public class KFCPackageBuilder {
private static final String TAG = KFCPackageBuilder.class.getSimpleName();
KFCSetMeal kfcSetMeal = new KFCSetMeal();
KFCSetMealParams kfcSetMealParams = new KFCSetMealParams();
public KFCPackageBuilder setChips(Chips chips) {
kfcSetMealParams.chips = chips;
return this;
}
public KFCPackageBuilder setDrinks(Drinks drinks) {
kfcSetMealParams.drinks = drinks;
return this;
}
public KFCPackageBuilder setHamburger(Hamburger hamburger) {
kfcSetMealParams.hamburger = hamburger;
return this;
}
public KFCSetMeal build() {
Log.e(TAG,kfcSetMealParams.chips.name+"--" + kfcSetMealParams.drinks.name + "--" + kfcSetMealParams.hamburger.name);
kfcSetMeal.apply(kfcSetMealParams);
return kfcSetMeal;
}
class KFCSetMealParams{
public Chips chips;
public Drinks drinks;
public Hamburger hamburger;
}
}
/**
* 套餐包含薯条、饮料、汉堡
*
* Created by Administrator on 2017/10/22.
*/
public class KFCSetMeal {
private Chips chips;
private Drinks drinks;
private Hamburger hamburger;
public void apply(KFCPackageBuilder.KFCSetMealParams kfcSetMealParams){
chips = kfcSetMealParams.chips;
drinks = kfcSetMealParams.drinks;
hamburger = kfcSetMealParams.hamburger;
}
}
5.3 链式调用
具体代码:
//肯德基的前台,组合工作餐
new KFCPackageBuilder()
.setChips(new Chips("工作餐薯条"))
.setDrinks(new Drinks("工作餐饮料"))
.setHamburger(new Hamburger("工作餐汉堡"))
.build();
//肯德基的前台,组合普通套餐
new KFCPackageBuilder()
.setChips(new Chips("普通套餐薯条"))
.setDrinks(new Drinks("普通套餐饮料"))
.setHamburger(new Hamburger("普通套餐汉堡"))
.build();
运行结果:
10-22 06:32:30.654 9364-9364/? E/KFCPackageBuilder: 工作餐薯条--工作餐饮料--工作餐汉堡
10-22 06:32:30.654 9364-9364/? E/KFCPackageBuilder: 普通套餐薯条--普通套餐饮料--普通套餐汉堡
5.4 AlertDialog的建造者模式剖析
基于SDK:23版本源码
P.apply(dialog.mAlert);
这一行是把AlertController
设置到AlertController.AlertParams
中,所以上面的Builder只是一个包装类,AlertController
才是真正的构建类,它在AlertDialog构造中已经初始化完成了。整理一下步骤:
AlertDialog
构造的时候初始化:AlertController
AlertDialog(Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) { super(context, createContextThemeWrapper ? resolveDialogTheme(context, themeResId) : 0, createContextThemeWrapper); mWindow.alwaysReadCloseOnTouchAttr(); mAlert = new AlertController(getContext(), this, getWindow());
}
AlertController
的内部类AlertController.AlertParams
用
来接收参数,最后赋值给AlertController
public static class AlertParams { public final Context mContext; public final LayoutInflater mInflater; public int mIconId = 0; public Drawable mIcon; public int mIconAttrId = 0; public CharSequence mTitle; public View mCustomTitleView; public CharSequence mMessage; public CharSequence mPositiveButtonText; ... }
在create方法中的关键代码:
P.apply(dialog.mAlert);
,代码内部给AlertController
去设置各种参数。public void apply(AlertController dialog) { if (mCustomTitleView != null) { dialog.setCustomTitle(mCustomTitleView); } else { if (mTitle != null) { dialog.setTitle(mTitle); } if (mIcon != null) { dialog.setIcon(mIcon); } if (mIconId >= 0) { dialog.setIcon(mIconId); } if (mIconAttrId > 0) { dialog.setIcon(dialog.getIconAttributeResId(mIconAttrId)); } } if (mMessage != null) { dialog.setMessage(mMessage); } ... }
Dialog的show方法,调用了子类
AlertDialog
的onCreate,这里接着调用了AlertController
的installContent
方法,真正的去创建view,在setupView中设置所有dialog的内容,添加到view上。到此为止,AlertDialog创建完成。Dialog的两种创建方式区别
不要把Builder的show()方法和Dialog的show()方法混淆。
// Builder.show()方式创建:内部调用了AlertDialog构造创建对象,然后调用dialog.show() new AlertDialog.Builder(this) .setTitle("确认") .setMessage("确定吗?") .setPositiveButton("是", null) .setNegativeButton("否", null) .show(); // builder.create().show()方式创建 AlertDialog.Builder builder = new AlertDialog.Builder(this); AlertDialog alertDialog = builder.create(); builder.setTitle("确认") .setMessage("确定吗?") .setPositiveButton("是", null) .setNegativeButton("否", null); alertDialog.show();
6. 总结
适用场景:
- 复杂对象的创建
- 可以创建不同的表示
- 一般情况建造者模式都是使用的其扩张方式