建造房子可以认为是一个建造者模式的实际场景。建房可以包括,建造地板、墙,房顶等。(每间房子都是这样的一个建造顺序,但细节是有区别的)
将一个复杂的对象的构建和它的表示分离,使得同样的构建过程可以创建不同的表示。 (使用多个简单的对象一步一步构建成一个复杂的对象)
例子:比如肯德基有很多套餐,同样都是由汉堡、饮料、薯条、甜筒组成,但他们的口味,价格都不一样。
/**
* Created by Administrator on 2017/10/22.
*/
public interface Builder {
// 炸薯条
public void createChips();
// 做饮料
public void createDrinks();
// 做汉堡
public void createHamburger();
// 组合套餐
public KFCSetMeal build();
}
/**
* 套餐包含薯条、饮料、汉堡
*
* 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;
}
}
/**
* 工作套餐
*
* 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;
}
}
/**
* 指导者
* Created by Administrator on 2017/10/22.
*/
public class KFCDirector {
public KFCSetMeal build(Builder builder){
builder.createChips();
builder.createDrinks();
builder.createHamburger();
return builder.build();
}
}
//肯德基的前台,组合工作餐
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: 普通套餐薯条–普通套餐饮料–普通套餐汉堡
标准的建造者模式,在工作中基本没有使用场景,使用较多的是将要介绍的这种链式创建者模式。
精简建造者中的角色,只保留具体建造者和具体产品,将产品中的get和set方法删除,避免调用者能够了解具体的实现逻辑。在具体建造者中创建一个内部类,copy具体产品的参数,在调用set方法时,将具体参数赋值到内部类中,在builder时再将具体参数copy到具体产品中,返回具体产品对象。实现效果如下:
new AlertDialog.Builder(self)
.setTitle("确认")
.setMessage("确定吗?")
.setPositiveButton("是", null)
.setNegativeButton("否", null)
.show();
删除抽象建造者和指导者,只保留具体建造者和具体的产品
- 给具体建造者内部加上和具体产品有完全相同参数的内部类,用来接收参数
- 改造设置参数方法,用内部类实例类接收,不真正添加到产品对象中
- 改造产品类,设置为成员变量设置为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;
}
}
具体代码:
//肯德基的前台,组合工作餐
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: 普通套餐薯条--普通套餐饮料--普通套餐汉堡
基于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();
适用场景: