内容基于effective java v2
举例来说:
一个手机会有很多模块,有些是必选:比如打电话,发短信;有些是可选的:比如WIFI,GPS,摄像头等等。那么Phone的构造函数应该如何实现呢
一种方式是重叠构造其模式
提供一个只含有“电话,短信”模块的构造器,然后提供含有一个可选参数的构造器,提供含有两个可选参数的构造器……稍懂点排列组合的人马上就能意识到,这么多构造器,写不起啊!就算写的起也用不起阿,谁知道每个构造器都是干什么的!
我们给出一个含有所有参数的构造器,不想设置的参数可以设置为一个无效的参数等,如果参数不多还好,不然一样会无法控制
另一种办法是只提供一个含有必选参数的构造器,其余的参数使用set方法设置。
这样的优势是容易创建、易于阅读。但是会失去一致性,也阻止了把类设计成不可变的,线程安全是个问题
第三种方案是使用Builder模式,不直接生成想要的对象,而是生成一个builder,让builder设置好所有需要的参数后build出来一个需要的对象
public class Phone { //Phone是我们最终要建立的对象 private Phone(boolean call, boolean sms){ this.call = call; this.sms = sms; } private boolean call; private boolean sms; private boolean mms; private boolean wifi; private boolean gps; private boolean camera; public static class Builder{ private boolean call; private boolean sms; private boolean mms; private boolean wifi; private boolean gps; private boolean camera; public Builder(boolean call, boolean sms){ this.call = call; this.sms = sms; } public Builder setMms(boolean mms){ this.mms = mms; return this; } public Builder setWfi(boolean wifi){ this.wifi = wifi; return this; } public Builder setGps(boolean gps){ this.gps = gps; return this; } public Builder setCamera(boolean camera){ this.camera = camera; return this; } public Phone build(){ Phone phone = new Phone(call, sms); phone.mms = this.mms; phone.wifi = this.wifi; phone.gps = this.gps; phone.camera = this.camera; return t; } } }
书中的例子在build函数如下
Phone phone = new Phone(this);
我觉得在build的时候对象t还没有对外开放,所以在构造函数之后复制和在其中复制是一样的。所以没有写成下面这样
private Phone(Builder builder){ this.call = builder.call; this.sms = builder.sms; }
书中之所以这么写还有一点原因是因为它的变量都为final的,不得不在构造函数中初始化,而我没有此修饰。
这里Phone的构造函数设置为private是防止外部任何形式的调用,想生成一个Phone对象,那么就通过Builder吧
android构建器的应用最常见的应该是AlertDialog和AlertDialog.Builder了吧
下面是email一个使用AlertDialog的代码片段
Dialog com.android.email.activity.AttachmentInfoDialog.onCreateDialog(Bundle savedInstanceState)
AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle(title); builder.setMessage(infoText); builder.setNeutralButton(R.string.okay_action, onClickListener); if (actionText != null && actionIntent != null) { builder.setPositiveButton(actionText, onClickListener); } return builder.show();
设置标题,设置信息,设置按钮,最后显示出来。
似乎完全符合上面所说的调用方式,那么下面看看内部是如何实现的
public static class Builder { private final AlertController.AlertParams P; private int mTheme; 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; } public Builder setTitle(int titleId) { P.mTitle = P.mContext.getText(titleId); return this; } public Builder setTitle(CharSequence title) { P.mTitle = title; return this; } ...... public Builder setIcon(int iconId) { P.mIconId = iconId; return this; } public Builder setIcon(Drawable icon) { P.mIcon = icon; return this; } ...... public AlertDialog create() { final AlertDialog dialog = new AlertDialog(P.mContext, mTheme, false); 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; } public AlertDialog show() { AlertDialog dialog = create(); dialog.show(); return dialog; } }
每个用来初始化的set都是把参数赋给private final AlertController.AlertParams P,然后返回this
能用来创建实例的有两个函数:create()和show(),其中show()调用了create()
实例化的时候调用了下面的构造函数
AlertDialog(Context context, int theme, boolean createContextWrapper) { super(context, resolveDialogTheme(context, theme), createContextWrapper); mWindow.alwaysReadCloseOnTouchAttr(); mAlert = new AlertController(getContext(), this, getWindow()); }
其中三个参数都是必选
之后设置其他参数,返回AlertDialog实例
期间调用了apply函数
P.apply(dialog.mAlert);
mAlert是AlertDialog的一个私有成员变量
private AlertController mAlert;
这里还涉及了AlertParams类,我们看看这个类都干了些什么
public static class AlertParams { 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 (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); } 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); } } } }
一目了然,就一个apply函数,用来设置AlertController的各种状态
我们可以在build方法中检查参数,看其是否满足约束条件。
builder的好处很多,其中一条就是不用写若干个方法来对应若干可选参数的组合。
builder也不需要考虑常规set带来的线程安全问题,而且使用set的类无法设计成不可变的。
但是builder也有弊端,比如需要创建Builder之后才能创建最终对象
转贴请保留以下链接
本人blog地址