Android设计模式——Builder模式

2019-09-29  本文已影响0人  如愿以偿丶

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
上一篇 下一篇

猜你喜欢

热点阅读