Android设计模式——Builder模式

本文深入解析Android中的Builder设计模式,探讨其应用场景与实现原理,对比链式调用区别,通过AlertDialog源码分析及手动实现导航栏示例,展示Builder模式在Android开发中的实践与优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.Builder模式介绍

  Builder模式又称建造者模式,将复杂对象的构建过程和表示过程进行分离,让其(参数)构建过程变得更加的简单和直观。

2.Builder模式使用场景

  1.一个复杂的对象,对象中的方法调用顺序不同产生了不同的作用,可以使用Builder模式
  2.当我们初始化一个特别复杂,参数很多的对象,且很多参数都具有默认值时,可以使用Builder模式

3.Builder模式和链式调用的区别

  Builder设计模式和链式调用是没有任何关系的

  3.1.Builder它是一种设计模式,但是它一般会采用链式调用的方式,并不是所有的链式调用都是Builder设计模式
  Builder模式体现形式:它一般都会有一个Builder对象,例如:Dialog,OkHttp,Retrofit,Glide等等都采用了Builder设计模式

  通过Dialog源码简单分析

	/**
	* AlertDialog :最终构建的Dialog对象
	* Bulider:用于构建我们的Dialog
	* AlertParams:用于存放我们设置的参数
	*/
  public class AlertDialog extends Dialog implements DialogInterface {
	//AlertController用来接收Builder 成员变量 P中的各个参数
    private AlertController mAlert;

	//构造函数
    protected AlertDialog(Context context) {
        this(context, 0);
    }


    AlertDialog(Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
        super(context, createContextThemeWrapper ? resolveDialogTheme(context, themeResId) : 0,
                createContextThemeWrapper);

        mWindow.alwaysReadCloseOnTouchAttr();
        mAlert = AlertController.create(getContext(), this, getWindow());
    }
    /**
    * 这就是最终设置的Title
    */
    @Override
    public void setTitle(CharSequence title) {
        super.setTitle(title);
        mAlert.setTitle(title);
    }

   
    /**
    * 这就是最终设置的Message
    */
    public void setMessage(CharSequence message) {
        mAlert.setMessage(message);
    }

	/**
	* 图标可以为
	*/
    public void setIcon(Drawable icon) {
        mAlert.setIcon(icon);
    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mAlert.installContent();
    }

	//**********************Builder 为AlertDialog静态内部类 ************************
    public static class Builder {
    	//存储 AlertDialog的各个参数 , icon title,message,button等等
        private final AlertController.AlertParams P;
        
        public Builder(Context context, int themeResId) {
            P = new AlertController.AlertParams(new ContextThemeWrapper(
                    context, resolveDialogTheme(context, themeResId)));
        }

      	// Builde采用了链式调用的方式,返回自身对象this
        public Builder setTitle(@StringRes int titleId) {
            P.mTitle = P.mContext.getText(titleId);
            return this;
        }
        
        public Builder setMessage(CharSequence message) {
            P.mMessage = message;
            return this;
        }

        public Builder setIcon(Drawable icon) {
            P.mIcon = icon;
            return this;
        }

        /**
        * 这里是create(),还有些是build()
        * 1.该方法之前只是设置(存储)一系列的参数
        * 2.create方法主要创建我们的Dialog对象,然后通过AlertParams将参数进行合并设置给我的Dialog,最终返回dialog对象。
        */
        public AlertDialog create() {
            // 1.创建我们的AlertDialog对象,
            final AlertDialog dialog = new AlertDialog(P.mContext, 0, false);
            // 2.将P中的参数进行合并应用到 dialog的mAlert对象中
            P.apply(dialog.mAlert);
           	....
            return dialog;
        }
    }
}

Dialog源码图解分析:
在这里插入图片描述

  3.2.链式调用它只是我们采用的一种调用方式
  链式调用体现形式:在调用方法的时候都会返回自身对象。
  这个我在之前的Android6.0运行权限框架封装中有使用到

	/**
	* 链式调用使用
	*/
	public class PermissionHelper {
	    //1.需要传什么参数
	    //1.1 Object Fragment 或 Activity  1.2 int 请求码  1.3 String[] 需要请求权限数组
	    private Object mObject;
	    private int mRequestCode;
	    private String[] mRequestPermission;
	
	     /**
    	 * 构造方法私有,防止外部new对象
   		 */
	    private PermissionHelper(Object object) {
	        this.mObject = object;
	    }
	
	
	    //1.我们经常使用的传递形式
	    public void requestPermission(Activity activity, int requestCode, String[] permissions) {
	        PermissionHelper.with(activity).requestCode(requestCode).requestPermission(permissions).request();
	    }
	
	
	    public void requestPermission(Fragment fragment, int requestCode, String[] permissions) {
	        PermissionHelper.with(fragment).requestCode(requestCode).requestPermission(permissions).request();
	    }
	
	
	    //2.使用链式调用形式
	    public static PermissionHelper with(Activity activity) {
	        return new PermissionHelper(activity);
	    }
	
	    //传Fragment
	    public static PermissionHelper with(Fragment fragment) {
	        return new PermissionHelper(fragment);
	    }
	
	    //传请求码,都会返回我们自身对象
	    public PermissionHelper requestCode(int requestCode) {
	        this.mRequestCode = requestCode;
	        return this;
	    }
	
	    //添加请求权限数组 String[]  都会返回我们自身对象
	    public PermissionHelper requestPermission(String... permissions) {
	        this.mRequestPermission = permissions;
	        return this;
	    }
	
	    //执行链式调用,执行我们的权限处理
	    public void request() {
	   		//....代码省略
	    }
	
	}

4. 手动实现一个导航栏

步骤:1.创建AbsNavigationBar我们的基类,利于后期的扩展
   2.Builder设计模式的内部静态Builder类
   3.存储Builder参数信息的类Params(今天就不写这个了,直接都写在Builder中)
   4.创建我们AbsNavigationBar的实现类NavigationBar

/**
 * NavigationBar的基类
 */
 public class AbsNavigationBar<B extends AbsNavigationBar.Builder> implements INavigationBar{
    private B mBuilder;
    private View mNavigationBar;
    /**
     * 子类创建Navigation是会调用父类的
     * 1.创建NavigationBar
     * 2.添加到父容器的第一个位置
     * 3.绑定参数
     * @param mBuilder
     */
    public AbsNavigationBar(B mBuilder) {
        this.mBuilder = mBuilder;

        createNavigationBar();
    }

    /**
     * 1.创建NavigationBar
     */
    @Override
    public void createNavigationBar(){
        //创建
        mNavigationBar = LayoutInflater.from(mBuilder.mContext).inflate(mBuilder.mLayoutId,mBuilder.mParent,false);
        //2.添加
        attachParent(mNavigationBar,mBuilder.mParent);
        //3.绑定参数
        attachNavigationParams();
    }

    /**
     * 2.添加到父容器的第一个位置
     * @param navigationBar
     * @param parent
     */
    @Override
    public void attachParent(View navigationBar, ViewGroup parent) {
        parent.addView(navigationBar,0);
    }

    /**
     * 3.绑定参数
     */
    @Override
    public void attachNavigationParams() {
        //进行循环设置文本
        Map<Integer,CharSequence> textMaps = mBuilder.mTextMaps;
        for (Map.Entry<Integer,CharSequence> entry : textMaps.entrySet()){
            TextView textView = findViewById(entry.getKey());
            textView.setText(entry.getValue());
        }

        //进行循环设置点击事件
        Map<Integer,View.OnClickListener> clickListenerMaps = mBuilder.mClickListenerMaps;
        for (Map.Entry<Integer,View.OnClickListener> entry : clickListenerMaps.entrySet()){
            View view = findViewById(entry.getKey());
            view.setOnClickListener(entry.getValue());
        }

    }

    public <T extends View> T findViewById(int viewId) {
       return (T) mNavigationBar.findViewById(viewId);
    }

    /**
     * 返回Builder
     * @return
     */
    public B getBuilder() {
        return mBuilder;
    }

    /**
     * 这次没有写保存数据的Params类,直接用Builder进行保存
     * @param <B>
     */
    public abstract static class Builder<B extends Builder> {
        public Context mContext;	//上下文
        public int mLayoutId;		//自定义布局文件
        public ViewGroup mParent;	//父容器

        public Map<Integer,CharSequence> mTextMaps;	//文本集合
        public Map<Integer,View.OnClickListener> mClickListenerMaps;	//点击事件集合

        public Builder(Context mContext, int mLayoutId, ViewGroup mParent) {
            this.mContext = mContext;
            this.mLayoutId = mLayoutId;
            this.mParent = mParent;
            mTextMaps = new HashMap<>();
            mClickListenerMaps = new HashMap<>();
        }

        /**
         * 创建我们的NavigationBar
         * @return
         */
        public abstract AbsNavigationBar create();

        public B setText(int viewId, String text){
            mTextMaps.put(viewId,text);
            return (B) this;
        }

        public B setOnClickListener(int viewId, View.OnClickListener clickListener){
            mClickListenerMaps.put(viewId,clickListener);
            return (B) this;
        }
    }
}
 public interface INavigationBar {
    /**
     * 1.创建NavigationBar
     */
    void createNavigationBar();

    /**
     * 2.添加到父容器的第一个位置
     * @param navigationBar
     * @param parent
     */
    void attachParent(View navigationBar, ViewGroup parent);

    /**
     * 3.绑定参数
     */
    void attachNavigationParams();
}
public class NavigationBar extends AbsNavigationBar {
    public NavigationBar(Builder mBuilder) {
        /**
         *  调用父类的构造方法
         *  1.创建NavigationBar
         *  2.添加到父容器的第一个位置
         *  3.绑定参数
         */
        super(mBuilder);
    }


    public static class Builder extends AbsNavigationBar.Builder<NavigationBar.Builder>{

        /**
         * @param mContext  上下文
         * @param mLayoutId 布局文件
         * @param mParent   父容器(需要将我们的布局添加到哪里)
         */
        public Builder(Context mContext, int mLayoutId, ViewGroup mParent) {
            //调用父类的Builder,进行保存参数信息
            super(mContext, mLayoutId, mParent);
        }

        /**
         * 创建NavigationBar
         * @return
         */
        @Override
        public NavigationBar create() {
            return new NavigationBar(this);
        }
    }
}

调用:

		//获取父容器
        ViewGroup parent  =  findViewById(R.id.parent);
        NavigationBar navigationBar = new NavigationBar.Builder(this,R.layout.navigation_bar,parent)
                .setText(R.id.tv_back,"返回")
                .setText(R.id.tv_title,"NavigationBar")
                .setOnClickListener(R.id.tv_back,new View.OnClickListener(){
                    @Override
                    public void onClick(View v) {
                        finish();
                    }
                })
                .create();

也可以设置默认导航栏,我们不需要关心控件id,只需要在NavigationBar中添加该方法就可以。

   public Builder(Context mContext, ViewGroup mParent) {
               super(mContext, R.layout.default_navigation_bar, mParent);
    }

5.总结

5.1 优点

  1.有良好的封装性,用户完全不必知道内部组成细节,完美结合了Java的迪米特原则(最少知识原则)
  2.容易扩展

5.2 缺点

  1.内部变化复杂,会有很多的Builder对象,消耗内存。

6.UML图

image.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值