一、模式定义
建造者模式(Builder Pattern):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
建造者模式是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。建造者模式属于对象创建型模式。因为翻译的不同,建造者模式又可以称为生成器模式。
举个栗子,之前有讲过ImageLoader的栗子,在ImageLoader中,提供了setImageCache、setLoadingImage、setLoadingFailed、setThreadCount、setPlaceHolder等方法,这些参数是ImageLoader类的成员变量,通过setter方法,让调用者设置这些变量的值,使这些参数都能个被用户自由定制。
但是就会出现问题:1、用户可以再任意时间,修改ImageLoader的配置,如果已经初始化过了线程池数量的情况下,再次调用setThreadCount 会出现什么结果??什么结果不重要,重要的是,这样提高了用户的使用成本,里面暴露 函数过多,需要用户再使用的时候仔细选择,会给使用的人造成困扰
二、模式结构
建造者模式包含如下角色(这些都是老逻辑了,新的实现方法,Builder是一个链式调用,本次setter后都返回本身,Director可以被省略):
- Builder:抽象建造者
- ConcreteBuilder:具体建造者
- Director:指挥者
- Product:产品角色
[图片上传失败...(image-d3db8f-1554990544119)]
三、时序图
[图片上传失败...(image-6e86dd-1554990544119)]
四、简单实现
图片加载的过程,步骤也很多,需要配置url、占位图、缩放类型、动画、进度条 等等,顺序是不固定的,下面就举一个项目中正在使用的例子,解释经典的Builder模式
图片加载时,参数配置:
package cc.kaipao.dongjia.imageloader;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.support.annotation.ColorInt;
import android.support.annotation.DrawableRes;
import cc.kaipao.dongjia.imageloader.base.LoadListener;
/**
* 图片加载信息配置类
*
* Created by XiaoFeng on 2017/3/14.
*/
public class ImageConfig {
private enum ScaleType {
FIT_CENTER,
CENTER_CROP,
FIT_START
}
private String url;
private @DrawableRes int placeholderResId;
private @DrawableRes int failureResId;
private ColorDrawable placeholderDrawable;
private ColorDrawable failureDrawable;
private ImageStyle style;
private float roundRadius;
private float borderWidth;
private @ColorInt int borderColor;
private boolean noFade;
private int fadeDuration;
private ScaleType scaleType;
private boolean needProgress;
private @DrawableRes int progressBgResId;
private ColorDrawable progressBgDrawable;
private LoadListener loadListener;
private int width;
private int height;
private ImageConfig(Builder builder) {
this.url = builder.url;
this.placeholderResId = builder.placeholderResId;
this.failureResId = builder.failureResId;
this.placeholderDrawable = builder.placeholderDrawable;
this.failureDrawable = builder.failureDrawable;
this.style = builder.style;
this.roundRadius = builder.roundRadius;
this.borderWidth = builder.borderWidth;
this.borderColor = builder.borderColor;
this.noFade = builder.noFade;
this.fadeDuration = builder.fadeDuration;
this.scaleType = builder.scaleType;
this.needProgress = builder.needProgress;
this.progressBgResId = builder.progressBgResId;
this.progressBgDrawable = builder.progressBgDrawable;
this.loadListener = builder.loadListener;
this.width = builder.width;
this.height = builder.height;
}
public static Builder builder() {
return new Builder();
}
public String getUrl() {
return url;
}
public int getPlaceholderResId() {
return placeholderResId;
}
public int getFailureResId() {
return failureResId;
}
public ColorDrawable getPlaceholderDrawable() {
return placeholderDrawable;
}
public ColorDrawable getFailureDrawable() {
return failureDrawable;
}
public boolean isNeedProgress() {
return needProgress;
}
public int getProgressBgResId() {
return progressBgResId;
}
public ColorDrawable getProgressBgDrawable() {
return progressBgDrawable;
}
public ImageStyle getStyle() {
return style;
}
public float getRoundRadius() {
return roundRadius;
}
public float getBorderWidth() {
return borderWidth;
}
public int getBorderColor() {
return borderColor;
}
public boolean isNoFade() {
return noFade;
}
public boolean isFitCenter() {
return scaleType == ScaleType.FIT_CENTER;
}
public boolean isCenterCrop() {
return scaleType == ScaleType.CENTER_CROP;
}
public int getFadeDuration() {
return fadeDuration;
}
public LoadListener getLoadListener() {
return loadListener;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public static class Builder {
// 图片url
private String url;
// 占位图
private @DrawableRes int placeholderResId;
// 失败图
private @DrawableRes int failureResId;
// 占位图
private ColorDrawable placeholderDrawable;
// 失败图
private ColorDrawable failureDrawable;
// 图片显示风格
private ImageStyle style;
// 圆角半径
private float roundRadius;
// 边框宽度
private float borderWidth;
// 边框颜色
private @ColorInt int borderColor;
// 是否是否淡入淡出
private boolean noFade;
// 淡入淡出时间间隔
private int fadeDuration;
// 缩放类型
private ScaleType scaleType;
// 是否显示进度条
private boolean needProgress;
// 进度条背景图
private @DrawableRes int progressBgResId;
// 进度条背景图
private ColorDrawable progressBgDrawable;
// 图片加载成功或失败的回调
private LoadListener loadListener;
/** 缩略图尺寸 */
private int width;
/** 缩略图尺寸 */
private int height;
public Builder() {
this.style = ImageStyle.NORMAL;
this.roundRadius = 0;
this.borderWidth = 0;
this.borderColor = Color.TRANSPARENT;
this.noFade = false;
this.fadeDuration = 0;
this.scaleType = ScaleType.CENTER_CROP;
this.needProgress = false;
this.width =0;
this.height = 0;
}
public Builder url(String url) {
this.url = url;
return this;
}
public Builder placeholderImage(@DrawableRes int placeholderResId) {
this.placeholderResId = placeholderResId;
return this;
}
public Builder failureImage(@DrawableRes int failureResId) {
this.failureResId = failureResId;
return this;
}
public Builder placeholderImage(ColorDrawable placeholderDrawable) {
this.placeholderDrawable = placeholderDrawable;
return this;
}
public Builder failureImage(ColorDrawable failureDrawable) {
this.failureDrawable = failureDrawable;
return this;
}
@Deprecated
public Builder needProgress() {
this.needProgress = true;
return this;
}
public Builder progressBackground(@DrawableRes int progressBgResId) {
this.progressBgResId = progressBgResId;
return this;
}
public Builder progressBackground(ColorDrawable progressBgDrawable) {
this.progressBgDrawable = progressBgDrawable;
return this;
}
public Builder style(ImageStyle style) {
this.style = style;
return this;
}
public Builder roundRadius(float radius) {
this.roundRadius = radius;
return this;
}
public Builder borderWidth(float borderWidth) {
this.borderWidth = borderWidth;
return this;
}
public Builder borderColor(@ColorInt int borderColor) {
this.borderColor = borderColor;
return this;
}
public Builder noFade() {
this.noFade = true;
return this;
}
public Builder fade(int duration) {
this.fadeDuration = duration;
return this;
}
public Builder fitCenter() {
this.scaleType = ScaleType.FIT_CENTER;
return this;
}
public Builder fitStart() {
this.scaleType = ScaleType.FIT_START;
return this;
}
public Builder centerCrop() {
this.scaleType = ScaleType.CENTER_CROP;
return this;
}
public Builder width(int width) {
this.width = width;
return this;
}
public Builder height(int height) {
this.height = height;
return this;
}
public Builder loadListener(LoadListener loadListener) {
this.loadListener = loadListener;
return this;
}
public ImageConfig build() {
return new ImageConfig(this);
}
}
}
在看看看使用的人是怎么使用的
ImageConfig config2 = ImageConfig.builder()
.url(UserAvatarUtil.getUrl(item.getZavatar()))
.placeholderImage(R.drawable.icon_set_avatar)
.failureImage(R.drawable.icon_set_avatar)
.style(ImageStyle.CIRCLE)
.build();
ivAvatar.setImageWithConfig(config2);
五、Android源码中模式实现
在Android源码中,我们最常用到的Builder模式就是AlertDialog.Builder, 使用该Builder来构建复杂的AlertDialog对象。简单示例如下 :
//显示基本的AlertDialog
private void showDialog(Context context) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setIcon(R.drawable.icon);
builder.setTitle("Title");
builder.setMessage("Message");
builder.setPositiveButton("Button1",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
setTitle("点击了对话框上的Button1");
}
});
builder.setNeutralButton("Button2",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
setTitle("点击了对话框上的Button2");
}
});
builder.setNegativeButton("Button3",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
setTitle("点击了对话框上的Button3");
}
});
builder.create().show(); // 构建AlertDialog, 并且显示
}
结果 : [图片上传失败...(image-928a13-1554990544119)]
下面我们看看AlertDialog的相关源码 :
// AlertDialog
public class AlertDialog extends Dialog implements DialogInterface {
// Controller, 接受Builder成员变量P中的各个参数
private AlertController mAlert;
// 构造函数
protected AlertDialog(Context context, int theme) {
this(context, theme, true);
}
// 4 : 构造AlertDialog
AlertDialog(Context context, int theme, boolean createContextWrapper) {
super(context, resolveDialogTheme(context, theme), createContextWrapper);
mWindow.alwaysReadCloseOnTouchAttr();
mAlert = new AlertController(getContext(), this, getWindow());
}
// 实际上调用的是mAlert的setTitle方法
@Override
public void setTitle(CharSequence title) {
super.setTitle(title);
mAlert.setTitle(title);
}
// 实际上调用的是mAlert的setCustomTitle方法
public void setCustomTitle(View customTitleView) {
mAlert.setCustomTitle(customTitleView);
}
public void setMessage(CharSequence message) {
mAlert.setMessage(message);
}
// AlertDialog其他的代码省略
// ************ Builder为AlertDialog的内部类 *******************
public static class Builder {
// 1 : 存储AlertDialog的各个参数, 例如title, message, icon等.
private final AlertController.AlertParams P;
// 属性省略
/**
* Constructor using a context for this builder and the {@link AlertDialog} it creates.
*/
public Builder(Context context) {
this(context, resolveDialogTheme(context, 0));
}
public Builder(Context context, int theme) {
P = new AlertController.AlertParams(new ContextThemeWrapper(
context, resolveDialogTheme(context, theme)));
mTheme = theme;
}
// Builder的其他代码省略 ......
// 2 : 设置各种参数
public Builder setTitle(CharSequence title) {
P.mTitle = title;
return this;
}
public Builder setMessage(CharSequence message) {
P.mMessage = message;
return this;
}
public Builder setIcon(int iconId) {
P.mIconId = iconId;
return this;
}
public Builder setPositiveButton(CharSequence text, final OnClickListener listener) {
P.mPositiveButtonText = text;
P.mPositiveButtonListener = listener;
return this;
}
public Builder setView(View view) {
P.mView = view;
P.mViewSpacingSpecified = false;
return this;
}
// 3 : 构建AlertDialog, 传递参数
public AlertDialog create() {
// 调用new AlertDialog构造对象, 并且将参数传递个体AlertDialog
final AlertDialog dialog = new AlertDialog(P.mContext, mTheme, false);
// 5 : 将P中的参数应用的dialog中的mAlert对象中
P.apply(dialog.mAlert);
dialog.setCancelable(P.mCancelable);
if (P.mCancelable) {
dialog.setCanceledOnTouchOutside(true);
}
dialog.setOnCancelListener(P.mOnCancelListener);
if (P.mOnKeyListener != null) {
dialog.setOnKeyListener(P.mOnKeyListener);
}
return dialog;
}
}
}
可以看到,通过Builder来设置AlertDialog中的title, message, button等参数, 这些参数都存储在类型为AlertController.AlertParams的成员变量P中,AlertController.AlertParams中包含了与之对应的成员变量。在调用Builder类的create函数时才创建AlertDialog, 并且将Builder成员变量P中保存的参数应用到AlertDialog的mAlert对象中,即P.apply(dialog.mAlert)代码段。我们看看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);
}
}
}
实际上就是把P中的参数挨个的设置到AlertController中, 也就是AlertDialog中的mAlert对象。从AlertDialog的各个setter方法中我们也可以看到,实际上也都是调用了mAlert对应的setter方法。在这里,Builder同时扮演了上文中提到的builder、ConcreteBuilder、Director的角色,简化了Builder模式的设计。
六、优缺点
优点
- 良好的封装性, 使用建造者模式可以使客户端不必知道产品内部组成的细节;
- 建造者独立,容易扩展;
- 在对象创建过程中会使用到系统中的一些其它对象,这些对象在产品对象的创建过程中不易得到。
缺点
- 会产生多余的Builder对象,消耗内存;
- 对象的构建过程暴露。