设计模式--Builder模式

Builder模式

模式介绍

  • Builder模式是一步一步创建一个复杂对象的创建型模式,它允许用户在不知道内部构建细节的情况下可以更精确的控制对象的构造流程
  • 该模式是为了将构建复杂对象的过程和他的部件解耦,使得构建过程和部件的表示隔离开来。
  • 因为一个对象可能有很多个组成部分,比如说汽车有车轮,方向盘,发动机,还有各种小零件等,如何将这些部件组装成一辆汽车,这个过程是相当漫长的,对于这种情况,为了在构建过程中对外部隐藏实现细节,就可以使用Builder模式将部件和组装过程分离,使得构建过程和部件可自由扩展,两者之间的耦合也降到最低

定义

  • 将一个对象的构建与他的表示分离,使得同样的构建可以创建不同的表示

使用场景

    1. 相同的方法,不同的执行顺序,产生不同的事件结果时
  • 2.多个部件都可以装配到一个对象中,但是产生的运行结果又不相同
  • 3.产品类非常复杂,或者产品类中的调用顺序不同产生了不同的作用,这个时候使用建造者模式非常合适
  • 4.当初始化一个对象特别复杂,如参数多,且很多参数都具有默认值时

UML图

这里写图片描述

  • Product产品类 – 产品的抽象类
  • Builder–抽象Builder类,规范产品的组件,一般由子类实现具体的 组件过程
  • ConcreteBuilder–具体的Builder类
  • Director – 统一的组装过程

简单实现

  • 计算机的组装过程较为复杂,并且组装顺序是不固定的,为了便于理解,我们把计算机组装的过程分为构建主机,设置操作系统,设置显示器三个部分,然后通过Director和具体的Builder来构建计算机对象,实例如下:
  • 我先来说说这种模式的工作流程,比方说是建造电脑,首先我们得提供一个造电脑的机器,机器有了我们还需要一个控制机器的人,然后就可以通过人给机器里面加原料来造电脑了
  • 那先来看看这个造电脑的机器吧
//抽象Builder类
public abstract class Builder {

    //设置主机
    public abstract void buildBoard(String board);

    //设置显示器
    public abstract void buildDisplay(String display);

    //设置操作系统
    public abstract void buildOS();

    //创建电脑
    public abstract Computer create();
}
  • 有人一看?诶?你这咋是抽象类啊?什么意思啊,抽象类怎么造 啊?
  • 诶,诸君莫急嘛, 想想看,我们如果直接把造电脑的机器写死,只能造一种电脑,比方说造Mac,那么以后假如又想造别的牌子的电脑怎么办?因为电脑的构成都是一样的,所以我们在这里先创建一个造电脑机器的模型(抽象类),我们可以通过给这个模型里面添点东西就可以造成电脑了(具体实现类),就像下面这样子
//具体的Builder类
public class MacBookBuilder extends Builder {

//这里的Macbook类的实现下面会说到,这里先不着急
    private Computer mComputer = new Macbook();


    @Override
    public void buildBoard(String board) {
        mComputer.setBoard(board);
    }

    @Override
    public void buildDisplay(String display) {
        mComputer.setDisplay(display);
    }

    @Override
    public void buildOS() {
        mComputer.setOs();
    }

    @Override
    public Computer create() {
        return mComputer;
    }
}
  • 比方说我们在这里造Macbook这种电脑,这就是我们的具体的机器了
  • 不过大家可能有点疑惑,上面的Macbook是哪来的呢?不是说好了造电脑的机器已经创建好了吗?对啊?可是你机器好了,电脑总得有模板吧 ?
//计算机抽象类,即Product角色
public abstract class Computer {

    protected String mBoard;
    protected String mDisplay;
    protected String mOs;

    protected Computer() {}

    //设置主板
    public void setBoard(String board) {
        mBoard = board;
    }

    //设置显示器
    public void setDisplay(String display) {
        mDisplay = display;
    }

    //设置操作系统
    public abstract void setOs();

    @Override
    public String toString() {
        return "Computer [mBoard = "+ mBoard+" ,mDisplay = "+mDisplay + " ,mOs = "+ mOs+ "]";
    }
}
  • 构建我们自己的电脑
public class Macbook extends Computer {

    protected Macbook() {
    }

    @Override
    public void setOs() {
        mOs = "Mac OS X 10.10";
    }
}
  • 这里的Macbook 实现类其实不只是这么简单,因为这是我们自己的电脑实现类,所以我们有权在这里对电脑的部件进行个性化(在这里就是对电脑构建的方法进行再包装,比如说这里电脑的参数都是字符串,我们就可以在这里对这些字符串加上自己的标识)
  • 然后造电脑的机器已经建成了,并且将特定类型的电脑的模板已经放在了电脑的机器里面
  • 我们看看操作机器的人的实现吧
public class Director {
    Builder mBuilder = null;

    public Director(Builder builder) {
        mBuilder = builder;
    }

    public void construct(String board,String display){
        mBuilder.buildBoard(board);
        mBuilder.buildDisplay(display);
        mBuilder.buildOS();
    }
}
  • 看到了吗?在他的构造方法里面拿到Builder,然后自己写一个构造方法,表明这个机器运行的次序
  • 看一下使用步骤
public static void main(String[] args) {
        Builder builder = new MacBookBuilder();
        Director director = new Director(builder);

        director.construct("因特尔主板","Retina显示器");
        System.out.println(builder.create().toString());

    }
  • 感觉大家应该看到这里应该豁然开朗了吧
  • 这里的过程并不复杂,只不过大家要理解他的整体的创建过程,以及每一部分的意义所在,可以看到这个建造者模式是将所有模块都尽可能的分离化,这样也降低了耦合程度,比如说电脑的模板类不需要关心建造,只需要知道此类电脑有什么不一样的结构实现一下
  • 而电脑的机器类则是关心每一个部件的构建细节
  • 作为操作机器的人,我们需要知道电脑部件之间的先后顺序

Android源码中Builder模式的实现

  • 在Android源码中,最常用的Builder模式就是AlertDialog.Builder,使用该Builder来构建复杂的AlertDialog对象,在开发过程中,我们经常用到AlertDialog,一般用法如下
AlertDialog.Builder builder = new AlertDialog.Builder(context);
        builder.setIcon(R.drawable.ic_test);
        builder.setTitle("sadas");
        builder.setPositiveButton("button1", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Log.d(TAG, "onClick: 点击了对话框上的按钮");
            }
        });
        builder.setNeutralButton("button2", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Log.d(TAG, "onClick: 点击了对话框上的按钮");
            }
        });
        builder.create().show();
  • 从他的使用方法可以很清晰的看出来,这是用的Builder模式,通过Builder来组建Dialog的各个部分,然后再create,
  • 先去看看AlertDialog.Builder里面吧(这个Builder是AlertDialog的内部静态类)
public static class Builder {
    private final AlertController.AlertParams P;
    public Builder(Context context) {
           this(context, resolveDialogTheme(context, ResourceId.ID_NULL));
    }
    public Builder(Context context, int themeResId) {
            P = new AlertController.AlertParams(new ContextThemeWrapper(
                    context, resolveDialogTheme(context, themeResId)));
        }
        public Builder setTitle(CharSequence title) {
            P.mTitle = title;
            return this;
        }
        public Builder setCustomTitle(View customTitleView) {
            P.mCustomTitleView = customTitleView;
            return this;
        }
        public Builder setIcon(Drawable icon) {
            P.mIcon = icon;
            return this;
        }
        //更多的set方法就不贴了,都是一样的

    public AlertDialog create() {
            // Context has already been wrapped with the appropriate theme.
            final AlertDialog dialog = new AlertDialog(P.mContext, 0, false);
            P.apply(dialog.mAlert);
            dialog.setCancelable(P.mCancelable);
            if (P.mCancelable) {
                dialog.setCanceledOnTouchOutside(true);
            }
            dialog.setOnCancelListener(P.mOnCancelListener);
            dialog.setOnDismissListener(P.mOnDismissListener);
            if (P.mOnKeyListener != null) {
                dialog.setOnKeyListener(P.mOnKeyListener);
            }
            return dialog;
        }

    public AlertDialog show() {
            final AlertDialog dialog = create();
            dialog.show();
            return dialog;
        }
}
  • 还记得我们刚才是设置了一大堆set之后,set的数据其实是保存在AlertController.AlertParams类型的参数中的,然后调用create方法返回了一个dialog实例,然后调用他的show方法
  • 但是?之前不是设置了很多属性嘛?怎么在create方法里面就看到了几个而已?其他的呢?不知道大家有没有注意到P.apply(dialog.mAlert);这句话,我们点进去,看看AlertController的内部类AlertParams里面有什么东西
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;
        public DialogInterface.OnClickListener mPositiveButtonListener;
        public CharSequence mNegativeButtonText;
        public DialogInterface.OnClickListener mNegativeButtonListener;
        public CharSequence mNeutralButtonText;
        public DialogInterface.OnClickListener mNeutralButtonListener;
        public boolean mCancelable;
        public DialogInterface.OnCancelListener mOnCancelListener;
        public DialogInterface.OnDismissListener mOnDismissListener;
        public DialogInterface.OnKeyListener mOnKeyListener;
        public CharSequence[] mItems;
        public ListAdapter mAdapter;
        public DialogInterface.OnClickListener mOnClickListener;
        public int mViewLayoutResId;
        public View mView;
        public int mViewSpacingLeft;
        public int mViewSpacingTop;
        public int mViewSpacingRight;
        public int mViewSpacingBottom;
        public boolean mViewSpacingSpecified = false;
        public boolean[] mCheckedItems;
        public boolean mIsMultiChoice;
        public boolean mIsSingleChoice;
        public int mCheckedItem = -1;
        public DialogInterface.OnMultiChoiceClickListener mOnCheckboxClickListener;
        public Cursor mCursor;
        public String mLabelColumn;
        public String mIsCheckedColumn;
        public boolean mForceInverseBackground;
        public AdapterView.OnItemSelectedListener mOnItemSelectedListener;
        public OnPrepareListViewListener mOnPrepareListViewListener;
        public boolean mRecycleOnMeasure = true;
}
  • 先看到的是很多dialog需要的参数
  • 我们再去看看他的apply方法
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);
            }
            if (mPositiveButtonText != null) {
                dialog.setButton(DialogInterface.BUTTON_POSITIVE, mPositiveButtonText,
                        mPositiveButtonListener, null);
            }
            if (mNegativeButtonText != null) {
                dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mNegativeButtonText,
                        mNegativeButtonListener, null);
            }
            if (mNeutralButtonText != null) {
                dialog.setButton(DialogInterface.BUTTON_NEUTRAL, mNeutralButtonText,
                        mNeutralButtonListener, null);
            }
            if (mForceInverseBackground) {
                dialog.setInverseBackgroundForced(true);
            }
            // For a list, the client can either supply an array of items or an
            // adapter or a cursor
            if ((mItems != null) || (mCursor != null) || (mAdapter != null)) {
                createListView(dialog);
            }
            if (mView != null) {
                if (mViewSpacingSpecified) {
                    dialog.setView(mView, mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight,
                            mViewSpacingBottom);
                } else {
                    dialog.setView(mView);
                }
            } else if (mViewLayoutResId != 0) {
                dialog.setView(mViewLayoutResId);
            }
        }
  • 然后将这些参数设置给dialog
  • 然后呢?然后就是show方法了啊,这里的AlertDialog的show方法调用的是他父类Dialog的show方法
  • show方法里面就是创建一个Window,然后显示,关于Window的简单分析,大家可以看我的这篇博客: android之Window学习

分析总结

  • 拿这个AlertDialog的过程跟我们刚才写的示例对比下,基本没啥区别吧?这里的AlertController.AlertParams就相当于是我们之前说过的电脑的模板,也就是电脑都需要的东西,造电脑的机器就是AlertDialog的内部类Builder了,而这里具体操作创建的过程就由Builder的create方法和AlertController.AlertParams的apply方法来完成了
  • 而且通过在Builder的set方法之后返回this(也就是返回自身),这样的话我们就可以级联调用,增加程序的简洁性
  • 那么,基于此的话,我们很容易的发现,当我们的各个部件的set方法
  • 好了,Builder的设计模式到这里就结束
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值