建造者模式

简述

建造者模式也成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个了。

工厂方法模式

适合种类繁多,但是生产过程相同的情况。定义一个抽象工厂类,或者工厂接口,实现类必须重写父类的方法来完成生产过程,直接返回用户需要的对象实体就行了。

建造者模式

侧重点就在使用者一方,使用者可以根据需要,通过建造者定制一个自己需要的对象来使用。

最后总结,前两种,侧重点在工厂自己生产,最后的建造者侧重点是使用者定制生产。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值