构建模式实现与研究 - bei1999/work GitHub Wiki

定义

通过组件的装配一步一步完成目的对象的构建过程

场景模拟

  • android 系统的AlertDialog 源码分析
  • 项目中的实践

实现

  • android 系统的AlertDialog 源码分析
/**
 * author : lzb
 * e-mail :
 * time   : 2017/12/07
 * desc   : 主类调用
 * version: 1.0
 */
public class Test extends Activity{
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_clock_view);

        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setIcon(R.drawable.psts_background_tab);
        builder.setMessage("提示");
        AlertDialog dialog = builder.create();
        dialog.show();

    }
}
 //AlertDialog 中的Builder 实现为一个静态内部类

 public static class Builder {
        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 Builder(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 android.R.style#Theme_Material_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 android.R.style#ThemeOverlay_Material_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
         */
        public Builder(Context context, int themeResId) {
            P = new AlertController.AlertParams(new ContextThemeWrapper(
                    context, resolveDialogTheme(context, themeResId)));
        }

        /**
         * Returns a {@link Context} with the appropriate theme for dialogs created by this Builder.
         * Applications should use this Context for obtaining LayoutInflaters for inflating views
         * that will be used in the resulting dialogs, as it will cause views to be inflated with
         * the correct theme.
         *
         * @return A Context for built Dialogs.
         */
        public Context getContext() {
            return P.mContext;
        }

        /**
         * Set the title using the given resource id.
         *
         * @return This Builder object to allow for chaining of calls to set methods
         */
        public Builder setTitle(@StringRes int titleId) {
            P.mTitle = P.mContext.getText(titleId);
            return this;
        }

        /**
         * Set the title displayed in the {@link Dialog}.
         *
         * @return This Builder object to allow for chaining of calls to set methods
         */
        public Builder setTitle(CharSequence title) {
            P.mTitle = title;
            return this;
        }

create 方法进行了类实例化构造

 /**
         * 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.
         */
        public AlertDialog create() {
            // Context has already been wrapped with the appropriate theme.
            final AlertDialog dialog = new AlertDialog(P.mContext, 0, false);
            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;
        }


小结

AlertDialog类的过程隐藏了类的内部实现细节,程序员只需要按照AlertDialog固定的方式去使用Builder设计模式封装的类即可,每一个setXXX()方法,发现统一返回当前对象,通过当前对象去调用另一个setXXX()方法,然后继续调用,这也是为什么使用Builder设计模式的例子,可以支持链式调用。

项目中的实践

/**
 * author : lzb
 * e-mail :
 * time   : 2017/12/07
 * desc   : build 模式封装参数
 * version: 1.0
 */

public abstract class Builder {

    public abstract Builder setRadius(float radius) ;

    public abstract Builder setTextSize(float textSize) ;

    public abstract Builder setBackgroundColor(int backgroundColor) ;

    public abstract Builder setPadding(float padding) ;


    public abstract ClockViewImpl create() ;
}

/**
 * author : lzb
 * e-mail :
 * time   : 2017/12/07
 * desc   : 表盘
 * version: 1.0
 */

public abstract class ClockView extends View {
    public ClockView(Context context) {
        super(context);
    }

    public ClockView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }
    public abstract void paint(Canvas canvas);
}

package com.bei.test.builder;

import android.content.Context;
import android.graphics.Canvas;
import android.support.annotation.Nullable;
import android.util.AttributeSet;

/**
 * author : lzb
 * e-mail : [email protected]
 * time   : 2017/12/07
 * desc   : 表盘实现类
 * version: 1.0
 */
public class ClockViewImpl extends ClockView {
    private float mTextSize; //文字大小

    public ClockViewImpl(Context context) {
        super(context);
    }

    public ClockViewImpl(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public void paint(Canvas canvas) {
        //todo 绘制表盘
    }

    public static class BuilderImpl extends Builder {
        private Context context;
        private float textSize; //文字大小

        public BuilderImpl(Context context) {
            this.context = context;
        }

        @Override
        public Builder setRadius(float radius) {
            return this;
        }

        @Override
        public Builder setTextSize(float textSize) {
            this.textSize = textSize;
            return this;
        }

        @Override
        public Builder setBackgroundColor(int backgroundColor) {
            return this;
        }

        @Override
        public Builder setPadding(float padding) {
            return this;
        }

        @Override
        public ClockViewImpl create() {
            ClockViewImpl clockView = new ClockViewImpl(context);
            clockView.mTextSize  = textSize;

            return clockView;
        }
    }
}

 //模仿构造模式
        Builder builder1 = new ClockViewImpl.BuilderImpl(this);
        builder1.setTextSize(12f);
        ClockViewImpl clockViewImpl = builder1.create();

总结

我们自己通过自定义view 模拟了下系统的AlertDialog 的实现过程,区别是系统的AlertDialog 的创建是通过传入绑定的context,调用activity 中的windowmanager 上面产生的view,但builder 模式运用雷同。 优点:1、结构清晰,2、代码明了,3、方便维护

  • 结构清晰,Builder设计模式结构基本固定,一个目标对象,一个Builder对象
  • 代码明了,链式调用Builder提供的方法,自由组合多种组件,构建出多种不一样的目标产品
  • 方便维护,面向抽象或接口的设计原则,上面的比如想扩展个属性,直接在build 类中添加,外部调用无需修改