设计模式之----建造者模式(AlertDialog源码分析)

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 具体实现步骤

删除抽象建造者和指导者,只保留具体建造者和具体的产品

  1. 给具体建造者内部加上和具体产品有完全相同参数的内部类,用来接收参数
  2. 改造设置参数方法,用内部类实例类接收,不真正添加到产品对象中
  3. 改造产品类,设置为成员变量设置为private,让外部调用者不能拿到~,添加方法将参数设置到产品中
  4. 改造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构造中已经初始化完成了。整理一下步骤:

  1. 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());
    

    }

  2. 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;
    ...
    }
    
  3. 在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);
        }
        ...
        }
    
  4. Dialog的show方法,调用了子类AlertDialog的onCreate,这里接着调用了AlertControllerinstallContent方法,真正的去创建view,在setupView中设置所有dialog的内容,添加到view上。到此为止,AlertDialog创建完成。

  5. 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. 总结

适用场景:

  • 复杂对象的创建
  • 可以创建不同的表示
  • 一般情况建造者模式都是使用的其扩张方式
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值