一啸蜜、模式定義
建造者模式(Builder Pattern):將一個復(fù)雜對象的構(gòu)建與它的表示分離,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示实檀。
建造者模式是一步一步創(chuàng)建一個復(fù)雜的對象惶洲,它允許用戶只通過指定復(fù)雜對象的類型和內(nèi)容就可以構(gòu)建它們按声,用戶不需要知道內(nèi)部的具體構(gòu)建細(xì)節(jié)。建造者模式屬于對象創(chuàng)建型模式恬吕。因為翻譯的不同签则,建造者模式又可以稱為生成器模式。
舉個栗子铐料,之前有講過ImageLoader的栗子渐裂,在ImageLoader中,提供了setImageCache钠惩、setLoadingImage柒凉、setLoadingFailed、setThreadCount篓跛、setPlaceHolder等方法膝捞,這些參數(shù)是ImageLoader類的成員變量,通過setter方法愧沟,讓調(diào)用者設(shè)置這些變量的值蔬咬,使這些參數(shù)都能個被用戶自由定制。
但是就會出現(xiàn)問題:1央渣、用戶可以再任意時間计盒,修改ImageLoader的配置渴频,如果已經(jīng)初始化過了線程池數(shù)量的情況下芽丹,再次調(diào)用setThreadCount 會出現(xiàn)什么結(jié)果?卜朗?什么結(jié)果不重要拔第,重要的是,這樣提高了用戶的使用成本场钉,里面暴露 函數(shù)過多蚊俺,需要用戶再使用的時候仔細(xì)選擇,會給使用的人造成困擾
二逛万、模式結(jié)構(gòu)
建造者模式包含如下角色(這些都是老邏輯了泳猬,新的實(shí)現(xiàn)方法,Builder是一個鏈?zhǔn)秸{(diào)用宇植,本次setter后都返回本身得封,Director可以被省略):
- Builder:抽象建造者
- ConcreteBuilder:具體建造者
- Director:指揮者
- Product:產(chǎn)品角色
[圖片上傳失敗...(image-d3db8f-1554990544119)]
三、時序圖
[圖片上傳失敗...(image-6e86dd-1554990544119)]
四指郁、簡單實(shí)現(xiàn)
圖片加載的過程忙上,步驟也很多,需要配置url闲坎、占位圖疫粥、縮放類型茬斧、動畫、進(jìn)度條 等等梗逮,順序是不固定的项秉,下面就舉一個項目中正在使用的例子,解釋經(jīng)典的Builder模式
圖片加載時库糠,參數(shù)配置:
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;
// 圖片顯示風(fēng)格
private ImageStyle style;
// 圓角半徑
private float roundRadius;
// 邊框?qū)挾? private float borderWidth;
// 邊框顏色
private @ColorInt int borderColor;
// 是否是否淡入淡出
private boolean noFade;
// 淡入淡出時間間隔
private int fadeDuration;
// 縮放類型
private ScaleType scaleType;
// 是否顯示進(jìn)度條
private boolean needProgress;
// 進(jìn)度條背景圖
private @DrawableRes int progressBgResId;
// 進(jìn)度條背景圖
private ColorDrawable progressBgDrawable;
// 圖片加載成功或失敗的回調(diào)
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源碼中模式實(shí)現(xiàn)
在Android源碼中,我們最常用到的Builder模式就是AlertDialog.Builder瞬欧, 使用該Builder來構(gòu)建復(fù)雜的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("點(diǎn)擊了對話框上的Button1");
}
});
builder.setNeutralButton("Button2",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
setTitle("點(diǎn)擊了對話框上的Button2");
}
});
builder.setNegativeButton("Button3",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
setTitle("點(diǎn)擊了對話框上的Button3");
}
});
builder.create().show(); // 構(gòu)建AlertDialog, 并且顯示
}
結(jié)果 : [圖片上傳失敗...(image-928a13-1554990544119)]
下面我們看看AlertDialog的相關(guān)源碼 :
// AlertDialog
public class AlertDialog extends Dialog implements DialogInterface {
// Controller, 接受Builder成員變量P中的各個參數(shù)
private AlertController mAlert;
// 構(gòu)造函數(shù)
protected AlertDialog(Context context, int theme) {
this(context, theme, true);
}
// 4 : 構(gòu)造AlertDialog
AlertDialog(Context context, int theme, boolean createContextWrapper) {
super(context, resolveDialogTheme(context, theme), createContextWrapper);
mWindow.alwaysReadCloseOnTouchAttr();
mAlert = new AlertController(getContext(), this, getWindow());
}
// 實(shí)際上調(diào)用的是mAlert的setTitle方法
@Override
public void setTitle(CharSequence title) {
super.setTitle(title);
mAlert.setTitle(title);
}
// 實(shí)際上調(diào)用的是mAlert的setCustomTitle方法
public void setCustomTitle(View customTitleView) {
mAlert.setCustomTitle(customTitleView);
}
public void setMessage(CharSequence message) {
mAlert.setMessage(message);
}
// AlertDialog其他的代碼省略
// ************ Builder為AlertDialog的內(nèi)部類 *******************
public static class Builder {
// 1 : 存儲AlertDialog的各個參數(shù), 例如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 : 設(shè)置各種參數(shù)
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 : 構(gòu)建AlertDialog, 傳遞參數(shù)
public AlertDialog create() {
// 調(diào)用new AlertDialog構(gòu)造對象艘虎, 并且將參數(shù)傳遞個體AlertDialog
final AlertDialog dialog = new AlertDialog(P.mContext, mTheme, false);
// 5 : 將P中的參數(shù)應(yīng)用的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來設(shè)置AlertDialog中的title, message, button等參數(shù), 這些參數(shù)都存儲在類型為AlertController.AlertParams的成員變量P中野建,AlertController.AlertParams中包含了與之對應(yīng)的成員變量属划。在調(diào)用Builder類的create函數(shù)時才創(chuàng)建AlertDialog, 并且將Builder成員變量P中保存的參數(shù)應(yīng)用到AlertDialog的mAlert對象中,即P.apply(dialog.mAlert)代碼段候生。我們看看apply函數(shù)的實(shí)現(xiàn) :
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);
}
}
}
實(shí)際上就是把P中的參數(shù)挨個的設(shè)置到AlertController中同眯, 也就是AlertDialog中的mAlert對象。從AlertDialog的各個setter方法中我們也可以看到唯鸭,實(shí)際上也都是調(diào)用了mAlert對應(yīng)的setter方法须蜗。在這里,Builder同時扮演了上文中提到的builder目溉、ConcreteBuilder明肮、Director的角色,簡化了Builder模式的設(shè)計缭付。
六柿估、優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
- 良好的封裝性, 使用建造者模式可以使客戶端不必知道產(chǎn)品內(nèi)部組成的細(xì)節(jié)陷猫;
- 建造者獨(dú)立秫舌,容易擴(kuò)展;
- 在對象創(chuàng)建過程中會使用到系統(tǒng)中的一些其它對象绣檬,這些對象在產(chǎn)品對象的創(chuàng)建過程中不易得到足陨。
缺點(diǎn)
- 會產(chǎn)生多余的Builder對象,消耗內(nèi)存河咽;
- 對象的構(gòu)建過程暴露钠右。