简述
建造者模式也成Build模式,建造者模式也算工厂模式的一种,因为开发人员也是调用一个工厂,最后工厂生产一个对象给开发者使用。只不过有点区别,建造者模式是通过Build方式,然后用户通过多次设置最终产生一个符合要求的对象。也就相当于这个建造者已经有了整体框架,具体想要一个什么样子的产品,由用户自己根据需要定制生产出来。举个栗子,典型的建造者模式在Android里面就有一个,AlertDialog这个类,下面看看怎么使用。
简单使用
下面看看建造者模式的两种写法。功能相同,有两种写法,看看你喜欢那种。
// 先拆开写,好理解
//创建对象
AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
//设置属性
builder.setIcon(R.mipmap.ic_launcher) //设置一个图标
builder.setTitle("Dialog") //设置标题
builder.setMessage("这是一个AlertDialog") //这是显示消息
// 通过create方法创建出AlertDialog的真正对象
AlertDialog alertDialog = builder.create();
// 通过show方法,把这个AlertDialog显示出来
alertDialog.show();
// 一步到位写法,下面这种写法都不需要接收,直接就创建出来然后显示了,是不是看着很高级。
new AlertDialog.Builder(mContext)
.setIcon(R.mipmap.ic_launcher) //设置一个图标
.setTitle("Dialog") //设置标题
.setMessage("这是一个AlertDialog") //这是显示消息
.show(); //显示出来
Dialog源码分析
看完用法,研究一下为啥可以用上面这种看起来很高级的写法吧。下面把一些关键的源码贴出来
我直接在代码中写注释,想看的话,看我写的中文就行了,英文有能力的去翻译一下
public class AlertDialog extends AppCompatDialog implements DialogInterface {
//这里有个全局变量AlertController,这个东西是Alert控制器,Alert在html页面里就是一个提示框
final AlertController mAlert;
//下面三个三个构造方法,都将是protected的,也就是不能通过new方式创建对象。
//那么怎么才能创建出来呢。开上面的使用,应该能猜到会有一个Builder的内部类
protected AlertDialog(@NonNull Context context) {
this(context, 0);
}
/**
* Construct an AlertDialog that uses an explicit theme. The actual style
* that an AlertDialog uses is a private implementation, however you can
* here supply either the name of an attribute in the theme from which
* to get the dialog's style (such as {@link R.attr#alertDialogTheme}.
*/
protected AlertDialog(@NonNull Context context, @StyleRes int themeResId) {
super(context, resolveDialogTheme(context, themeResId));
mAlert = new AlertController(getContext(), this, getWindow());
}
protected AlertDialog(@NonNull Context context, boolean cancelable,
@Nullable OnCancelListener cancelListener) {
this(context, 0);
setCancelable(cancelable);
setOnCancelListener(cancelListener);
}
//这就是那个静态内部类,通过它就能创建出AlertDialog真正的对象
public static class Builder {
//这里又有一个Alert管理者的内部类,AlertParams 声明为P
//一会看看怎么用的
private final AlertController.AlertParams P;
/**
* Creates a builder for an alert dialog that uses the default alert
* dialog theme.
* <p>
* The default alert dialog theme is defined by
* {@link android.R.attr#alertDialogTheme} within the parent
* {@code context}'s theme.
*
* @param context the parent context
*/
//看到这里了吗,这个就是public的,这个就是供开发人员调用的构造方法
public Builder(@NonNull Context context) {
//这里有调用了自己的构造,那就往下看
this(context, resolveDialogTheme(context, 0));
}
/**
* Creates a builder for an alert dialog that uses an explicit theme
* resource.
* <p>
* The specified theme resource ({@code themeResId}) is applied on top
* of the parent {@code context}'s theme. It may be specified as a
* style resource containing a fully-populated theme, such as
* {@link R.style#Theme_AppCompat_Dialog}, to replace all
* attributes in the parent {@code context}'s theme including primary
* and accent colors.
* <p>
* To preserve attributes such as primary and accent colors, the
* {@code themeResId} may instead be specified as an overlay theme such
* as {@link R.style#ThemeOverlay_AppCompat_Dialog}. This will
* override only the window attributes necessary to style the alert
* window as a dialog.
* <p>
* Alternatively, the {@code themeResId} may be specified as {@code 0}
* to use the parent {@code context}'s resolved value for
* {@link android.R.attr#alertDialogTheme}.
*
* @param context the parent context
* @param themeResId the resource ID of the theme against which to inflate
* this dialog, or {@code 0} to use the parent
* {@code context}'s default alert dialog theme
*/
//执行到这里,给P赋值了,创建出P这个实体了,也就是Builder第一步的目的是创建P这个实体
public Builder(@NonNull Context context, @StyleRes int themeResId) {
P = new AlertController.AlertParams(new ContextThemeWrapper(
context, resolveDialogTheme(context, themeResId)));
mTheme = themeResId;
}
//下面贴几个方法就行了,不全贴出来,思想都一样
/**
* Set the title displayed in the {@link Dialog}.
* @return This Builder object to allow for chaining of calls to set methods
*/
// 看这个方法,设置Title,就是给P的mTitle属性赋值,然后把自己返回了
public Builder setTitle(@Nullable CharSequence title) {
P.mTitle = title;
return this;
}
/**
* Set the message to display.
* @return This Builder object to allow for chaining of calls to set methods
*/
// 这个方法也一样,同样是给P的mMessage属性赋值,然后返回自身
public Builder setMessage(@Nullable CharSequence message) {
P.mMessage = message;
return this;
}
/**
* Set the resource id of the {@link Drawable} to be used in the title.
* <p>
* Takes precedence over values set using {@link #setIcon(Drawable)}
* @return This Builder object to allow for chaining of calls to set methods
*/
//这个方法同样的给P的属性赋值,然后返回自身。也就是Builder的方法其实就是给自己的内部对象P赋值,然后返回自己,
// TODO 这是重点:这样就是一直在给自身的内部类P添加属性
public Builder setIcon(@DrawableRes int iconId) {
P.mIconId = iconId;
return this;
}
/**
* Creates an {@link AlertDialog} with the arguments supplied to this
* builder.
* <p>
* Calling this method does not display the dialog. If no additional
* processing is needed, {@link #show()} may be called instead to both
* create and display the dialog.
*/
//看这个create方法,这里返回的就是AlertDialog对象了
//这里才是真正的把东西建造出来
@NonNull
public AlertDialog create() {
// We can't use Dialog's 3-arg constructor with the createThemeContextWrapper param,
// so we always have to re-set the theme
// 这里是AlertDialog的内部类,所以这里可以直接new对象
final AlertDialog dialog = new AlertDialog(P.mContext, mTheme);
// 这里dialog.mAlert这个参数看到了吗,就是AlertDialog的全局变量,和P关联了一下
// 这里应该就是把P的属性添加到需要返回的AlertDialog实体里面,这样开发者设置的那些属性都生效了
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;
}
/**
* Creates an {@link AlertDialog} with the arguments supplied to this
* builder and immediately displays the dialog.
* <p>
* Calling this method is functionally identical to:
* <pre>
* AlertDialog dialog = builder.create();
* dialog.show();
* </pre>
*/
// 看我使用的第二种写法,是不是没有调用creat,同样能show出来
// 就是因为在show方法里面调用了create,所以不管你是不是调用了create,在你调用show方法的时候,都会调用create方法
public AlertDialog show() {
final AlertDialog dialog = create();
dialog.show();
return dialog;
}
}
}
所以以后大家自己开发的时候,如果遇到需要创建一个东西,但是有时候需要设置的属性比较多,有时候设置的属性比较少,这种情况就可以考虑建造者模式了。
总结一下:
简单工厂模式
比较适合数量很少,而且基本不会改变的情况。栗子:和底部导航栏结合显示的Fragment,一般也就3个、4个、最多5个了。
工厂方法模式
适合种类繁多,但是生产过程相同的情况。定义一个抽象工厂类,或者工厂接口,实现类必须重写父类的方法来完成生产过程,直接返回用户需要的对象实体就行了。
建造者模式
侧重点就在使用者一方,使用者可以根据需要,通过建造者定制一个自己需要的对象来使用。
最后总结,前两种,侧重点在工厂自己生产,最后的建造者侧重点是使用者定制生产。