美文网首页
对于使用构建者模式的思考

对于使用构建者模式的思考

作者: 刀放下好好说话 | 来源:发表于2018-05-23 14:45 被阅读88次

(本文仅代表个人观点,如果不对,请留言指正)

最近在看新的网络请求框架retrofit,看到他支持链式调用,也就是我们知道的构建者模式。好像很多开源的库都热衷于构建者模式,究竟是跟风还是确实有什么独特的好处呢?

我们通过一个案例体会一下他的好处。

案例一、提示弹框

开发过程中,我们经常需要弹出一个提示框:

这个弹框有标题、提示内容、取消按钮、确认按钮等,如果不使用构建者模式,我之前的封装是这样的:

public class NoticeDialogUtils {

    /**
     * 只响应一个按钮的dialog
     * @param context
     * @param msg
     */
    public static NoticeDialog notice0(Context context , String msg ){
        return notice(context, "提醒", msg, null, "确定", true, null, new NoticeDialog.OnNoticeClickListener() {
            @Override
            public void onNoticeClick(NoticeDialog dialog, int which) {
                dialog.dismiss();
            }
        });
    }

    /**
     * 只响应一个按钮的dialog
     * @param context
     * @param msg
     * @param listener
     */
    public static NoticeDialog notice1(Context context , String msg , NoticeDialog.OnNoticeClickListener listener){
        return notice(context, "提醒", msg, "取消", "确定", true, new NoticeDialog.OnNoticeClickListener() {
            @Override
            public void onNoticeClick(NoticeDialog dialog, int which) {
                dialog.dismiss();
            }
        }, listener);
    }


    /**
     * 只响应一个按钮的dialog  点击外部不可以取消
     * @param context
     * @param msg
     * @param listener
     */
    public static NoticeDialog notice1Cancel(Context context , String msg , boolean cancel,  NoticeDialog.OnNoticeClickListener listener){
        return notice(context, "提醒", msg, "取消", "确定", cancel, new NoticeDialog.OnNoticeClickListener() {
            @Override
            public void onNoticeClick(NoticeDialog dialog, int which) {
                dialog.dismiss();
            }
        }, listener);
    }

    /**
     * 响应两个按钮的dialog , 取消按钮除了dismiss()会执行操作
     * @param context
     * @param msg
     * @param listener
     */
    public static NoticeDialog notice2(Context context , String msg , NoticeDialog.OnNoticeClickListener listener){
        return notice(context, "提醒", msg, "取消", "确定", listener);
    }

    /**
     * 提示的dialog
     * @param context
     * @param title
     * @param msg
     * @param cancel
     * @param confirm
     * @param listener
     */
    public static NoticeDialog notice(Context context , String title , String msg , String cancel , String confirm , NoticeDialog.OnNoticeClickListener listener){
        return notice(context, title, msg, cancel, confirm, true, listener , listener);
    }

    /**
     * 提示的dialog  可以取消
     * @param context
     * @param title
     * @param msg
     * @param cancel
     * @param confirm
     * @param listener
     */
    public static NoticeDialog noticeCancel(Context context , String title , String msg , String cancel , String confirm , boolean cancelble, NoticeDialog.OnNoticeClickListener listener){
        return notice(context, title, msg, cancel, confirm, cancelble, listener , listener);
    }

    /**
     * 提示dialog
     * @param context
     * @param title
     * @param msg
     * @param cancel
     * @param confirm
     * @param cancelListener
     * @param confirmListener
     */
    private static NoticeDialog notice(Context context , String title , String msg , String cancel , String confirm ,boolean cancelble,
                              NoticeDialog.OnNoticeClickListener cancelListener , NoticeDialog.OnNoticeClickListener confirmListener){
        NoticeDialog.Builder builder = new NoticeDialog.Builder(context);
        NoticeDialog dialog = builder.setTitle(title)
                .setOutSideCancelble(cancelble)
                .setMessage(msg)
                .setPositiveButton(confirm, confirmListener)
                .setNegativeButton(cancel, cancelListener)
                .create();
        dialog.show();
        return dialog;
    }
}

然后调用的地方补充参数:

NoticeDialogUtils.notice(mContext, "提醒", "返回添加赛事,将放弃购买此保存订单", "取消", "确定", new NoticeDialog.OnNoticeClickListener() {
            @Override
            public void onNoticeClick(NoticeDialog dialog, int which) {
                switch (which) {
                    case NOTICE_CONFIRM:
                        dialog.dismiss();
                        onAddMatchClick();
                        break;
                    case NOTICE_CANCEL:
                        dialog.dismiss();
                        break;
                }
            }
        });

代码很简洁,只是传参的时候需要仔细对照api,可读性不太友好。

实际上,这样写已经算合格了,没有在需要弹框的时候去复制粘贴上一个弹框的代码。那么还能不能优化呢?我们看看利用构建者模式封装会是什么情况:

public class DialogUtils {

    public static DialogUtils instance;

    public static DialogUtils getInstance() {
        if (instance == null) {
            synchronized (DialogUtils.class) {
                if (instance == null) {
                    instance = new DialogUtils();
                }
            }
        }
        return instance;
    }

    public static class Builder {
        private Context context;
        private View layout;
        private int width;
        private int gravity;
        private int anim;
        private String title = "提示";
        private String notice = "";
        private String confirm = "确定";
        private String cancel = "取消";
        private boolean outsideCancel = false;
        private View.OnClickListener onClickListener;

        public Builder context(Context context) {
            this.context = context;
            return this;
        }

        public Builder layout(View layout) {
            this.layout = layout;
            return this;
        }

        public Builder width(int width) {
            this.width = width;
            return this;
        }

        public Builder gravity(int gravity) {
            this.gravity = gravity;
            return this;
        }

        public Builder anim(int anim) {
            this.anim = anim;
            return this;
        }

        public Builder outsideCancel(boolean outsideCancel) {
            this.outsideCancel = outsideCancel;
            return this;
        }

        public Builder onClickListener(View.OnClickListener onClickListener) {
            this.onClickListener = onClickListener;
            return this;
        }

        public Builder title(String title) {
            this.title = title;
            return this;
        }

        public Builder notice(String notice) {
            this.notice = notice;
            return this;
        }

        public Builder confirm(String confirm) {
            this.confirm = confirm;
            return this;
        }

        public Builder cancel(String cancel) {
            this.cancel = cancel;
            return this;
        }

        public DialogUtils build() {
            DialogUtils dialogUtils = DialogUtils.getInstance();
            if (context == null) {
                throw new IllegalArgumentException("context must be not null");
            }
            dialogUtils.context = this.context;
            if (layout == null) {
                LayoutInflater inflater = LayoutInflater.from(context);
                this.layout = inflater.inflate(R.layout.dialog_common_dialog_layout, null);
                dialogUtils.isUseDefaultLayout = true;
            }
            dialogUtils.layout = this.layout;
            if (width == 0) {
                width = (int) (context.getResources().getDisplayMetrics().widthPixels * 0.8);
            }
            dialogUtils.width = this.width;
            if (gravity == 0) {
                gravity = Gravity.CENTER;
            }
            dialogUtils.gravity = this.gravity;
            if(anim == 0){
                anim = R.style.dialog_default_anim;
            }
            dialogUtils.anim = this.anim;
            dialogUtils.title = this.title;
            dialogUtils.notice = this.notice;
            dialogUtils.confirm = this.confirm;
            dialogUtils.cancel = this.cancel;
            dialogUtils.outsideCancel = this.outsideCancel;
            dialogUtils.onClickListener = this.onClickListener;
            return dialogUtils;
        }
    }

    private Context context;
    private View layout;
    private int width;
    private int gravity;
    private int anim;
    private String title;
    private String notice;
    private String confirm;
    private String cancel;
    private boolean outsideCancel;
    private View.OnClickListener onClickListener;
    //是否使用默认的布局
    private boolean isUseDefaultLayout;

    public Dialog show() {
        Dialog dialog = new Dialog(context, R.style.loading_dialog);
        dialog.setContentView(layout);
        dialog.setCanceledOnTouchOutside(outsideCancel);
        Window window = dialog.getWindow();
        if (window != null) {
            window.getAttributes().width = width;
            window.setGravity(gravity);
            window.setWindowAnimations(anim);
        }
        if (isUseDefaultLayout) {
            TextView tvTitle = layout.findViewById(R.id.tv_title);
            TextView tvNotice = layout.findViewById(R.id.tv_notice);
            TextView tvCancel = layout.findViewById(R.id.tv_cancel);
            TextView tvConfirm = layout.findViewById(R.id.tv_confirm);
            tvTitle.setText(title);
            tvNotice.setText(notice);
            tvCancel.setText(cancel);
            tvConfirm.setText(confirm);
            tvConfirm.setOnClickListener(onClickListener);
            tvCancel.setOnClickListener(onClickListener);
        }
        dialog.show();
        return dialog;
    }
}

然后是调用的时候:

    new DialogUtils.Builder()
                .context(MainActivity.this)
                .title("更新")
                .notice("是否更新到最新版本?")
                .onClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {

                    }
                })
                .build()
                .show();

直观上,可读性非常强,api很灵活,参数之间可以随机组合。

疑问

通过案例我们可以得出一个结论,构建者的api很优雅,但是build()这个方法是不是必要的呢?
我们也可以直接在外部内return this;为什么一定要增加一个内部类呢?
如下的写法:

public class Out {

    private String param;

    public Out param(String param){
        this.param = param;
        return this;
    }

    public static class Inner{
        private String param;

        public Inner param(String param){
            this.param = param;
            return this;
        }

        public Out build(){
            Out out = new Out();
            out.param = param;
            return out;
        }
    }

    public void show(){

    }
}

    new Out.Inner().param("123").build();
    new Out().param("123");
    

我们上下两种方式都是可以传入参数的。那么这个build()方法是不是很多余呢?
自然是不多余。下面的写法在任何一次调用之后都是能拿到Out这个对象进行最终的方法show()的调用,但这是很危险的,因为参数还没有确定完全准备好,很可能发生空指针异常。
所以build()这个方法就可以进行风险规避,在每一个参数引用之前进行判断拦截:

    public DialogUtils build() {
            DialogUtils dialogUtils = DialogUtils.getInstance();
            if (context == null) {
                throw new IllegalArgumentException("context must be not null");
            }
            dialogUtils.context = this.context;
            if (layout == null) {
                LayoutInflater inflater = LayoutInflater.from(context);
                this.layout = inflater.inflate(R.layout.dialog_common_dialog_layout, null);
                dialogUtils.isUseDefaultLayout = true;
            }
            dialogUtils.layout = this.layout;
            if (width == 0) {
                width = (int) (context.getResources().getDisplayMetrics().widthPixels * 0.8);
            }
            dialogUtils.width = this.width;
            if (gravity == 0) {
                gravity = Gravity.CENTER;
            }
            dialogUtils.gravity = this.gravity;
            if(anim == 0){
                anim = R.style.dialog_default_anim;
            }
            dialogUtils.anim = this.anim;
            dialogUtils.title = this.title;
            dialogUtils.notice = this.notice;
            dialogUtils.confirm = this.confirm;
            dialogUtils.cancel = this.cancel;
            dialogUtils.outsideCancel = this.outsideCancel;
            dialogUtils.onClickListener = this.onClickListener;
            return dialogUtils;
        }

这就保证了代码的健壮性。

案例二、网络请求库的封装

正如上面提到的构建者的好处,所以我相信retrofit使用构建者肯定是考虑到了这一点,因为网络请求的时候参数的变化实在是太多了,如果使用传统的方法传参,将是一场灾难。所以赶紧改造自己的网络请求库吧。

代码比较少,不传github了。

相关文章

  • 对于使用构建者模式的思考

    (本文仅代表个人观点,如果不对,请留言指正) 最近在看新的网络请求框架retrofit,看到他支持链式调用,也就是...

  • 构建者(Builder)模式

    不怕跌倒,所以飞翔 本文中知识点概述: 构建者模式的定义以及为什么要使用构建者模式; 构建者模式在Android中...

  • 构建者模式

    构建者模式的定义 将一个复杂对象的构建和它的表示分离,使得同样的构建过程可以创建不同的表示。 构建者模式的使用场景...

  • 01- 构建者模式(Builder Pattern)

    设计模式 构建者模式 概述: 当创建复杂对象的时候,可以使用构建者模式,把创建复杂对象的过程分离出来,并且保持对象...

  • 建造者模式(生成器模式)

    建造者模式,就是将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。如果我们使用建造者模式,...

  • 构建者模式

    1.什么是构建者模式以及构建者模式的优点 构建者模式又称建造者模式,其主要功能是将一个复杂的对象的构建和表示进行分...

  • 设计模式(创建型)-- 建造者模式

    Builder 模式,中文翻译为建造者模式 或者 构建者模式,也有人叫它 生成器模式。 相对于普通的set方法,建...

  • 07.构建者模式(创建型)

    创建型模式-构建者模式(生成器模式) 构建者模式为客户返回一个由多个部件组成的复杂产品。 一、构建者模式相关定义 ...

  • 设计模式之建造者模式

    建造者模式:将一个复杂对象的构建与它的表示分离,是的同样的构建过程可以创建不同的表示 使用建造者模式,客户端只需要...

  • 23种设计模式-原型模式(拷贝)

    作用:拷贝。和构建者模式相似,构建者帮助我们把精力放在复杂的可配置项上,而原型模式使用在创建复杂的或者构造耗时的实...

网友评论

      本文标题:对于使用构建者模式的思考

      本文链接:https://www.haomeiwen.com/subject/zburjftx.html