對于使用構(gòu)建者模式的思考

(本文僅代表個人觀點,如果不對寡具,請留言指正)

最近在看新的網(wǎng)絡(luò)請求框架retrofit牍汹,看到他支持鏈?zhǔn)秸{(diào)用铐维,也就是我們知道的構(gòu)建者模式柬泽。好像很多開源的庫都熱衷于構(gòu)建者模式,究竟是跟風(fēng)還是確實有什么獨特的好處呢方椎?

我們通過一個案例體會一下他的好處聂抢。

案例一、提示彈框

開發(fā)過程中棠众,我們經(jīng)常需要彈出一個提示框:

這個彈框有標(biāo)題琳疏、提示內(nèi)容、取消按鈕闸拿、確認(rèn)按鈕等空盼,如果不使用構(gòu)建者模式,我之前的封裝是這樣的:

public class NoticeDialogUtils {

    /**
     * 只響應(yīng)一個按鈕的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();
            }
        });
    }

    /**
     * 只響應(yīng)一個按鈕的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);
    }


    /**
     * 只響應(yīng)一個按鈕的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);
    }

    /**
     * 響應(yīng)兩個按鈕的dialog , 取消按鈕除了dismiss()會執(zhí)行操作
     * @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;
    }
}

然后調(diào)用的地方補(bǔ)充參數(shù):

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;
                }
            }
        });

代碼很簡潔揽趾,只是傳參的時候需要仔細(xì)對照api,可讀性不太友好苛骨。

實際上篱瞎,這樣寫已經(jīng)算合格了,沒有在需要彈框的時候去復(fù)制粘貼上一個彈框的代碼痒芝。那么還能不能優(yōu)化呢俐筋?我們看看利用構(gòu)建者模式封裝會是什么情況:

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;
    //是否使用默認(rèn)的布局
    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;
    }
}

然后是調(diào)用的時候:

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

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

直觀上严衬,可讀性非常強(qiáng)澄者,api很靈活,參數(shù)之間可以隨機(jī)組合请琳。

疑問

通過案例我們可以得出一個結(jié)論粱挡,構(gòu)建者的api很優(yōu)雅,但是build()這個方法是不是必要的呢俄精?
我們也可以直接在外部內(nèi)return this;為什么一定要增加一個內(nèi)部類呢询筏?
如下的寫法:

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");
    

我們上下兩種方式都是可以傳入?yún)?shù)的。那么這個build()方法是不是很多余呢竖慧?
自然是不多余屈留。下面的寫法在任何一次調(diào)用之后都是能拿到Out這個對象進(jìn)行最終的方法show()的調(diào)用,但這是很危險的测蘑,因為參數(shù)還沒有確定完全準(zhǔn)備好,很可能發(fā)生空指針異常康二。
所以build()這個方法就可以進(jìn)行風(fēng)險規(guī)避碳胳,在每一個參數(shù)引用之前進(jìn)行判斷攔截:

    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;
        }

這就保證了代碼的健壯性。

案例二沫勿、網(wǎng)絡(luò)請求庫的封裝

正如上面提到的構(gòu)建者的好處挨约,所以我相信retrofit使用構(gòu)建者肯定是考慮到了這一點味混,因為網(wǎng)絡(luò)請求的時候參數(shù)的變化實在是太多了,如果使用傳統(tǒng)的方法傳參诫惭,將是一場災(zāi)難翁锡。所以趕緊改造自己的網(wǎng)絡(luò)請求庫吧。

代碼比較少夕土,不傳github了馆衔。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市怨绣,隨后出現(xiàn)的幾起案子角溃,更是在濱河造成了極大的恐慌,老刑警劉巖篮撑,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件减细,死亡現(xiàn)場離奇詭異,居然都是意外死亡赢笨,警方通過查閱死者的電腦和手機(jī)未蝌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來茧妒,“玉大人萧吠,你說我怎么就攤上這事∷晃埃” “怎么了怎憋?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長九昧。 經(jīng)常有香客問我绊袋,道長,這世上最難降的妖魔是什么铸鹰? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任癌别,我火速辦了婚禮,結(jié)果婚禮上蹋笼,老公的妹妹穿的比我還像新娘展姐。我一直安慰自己,他們只是感情好剖毯,可當(dāng)我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布圾笨。 她就那樣靜靜地躺著,像睡著了一般逊谋。 火紅的嫁衣襯著肌膚如雪擂达。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天胶滋,我揣著相機(jī)與錄音板鬓,去河邊找鬼悲敷。 笑死,一個胖子當(dāng)著我的面吹牛俭令,可吹牛的內(nèi)容都是我干的后德。 我是一名探鬼主播,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼抄腔,長吁一口氣:“原來是場噩夢啊……” “哼瓢湃!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起妓柜,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤箱季,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后棍掐,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體藏雏,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年作煌,在試婚紗的時候發(fā)現(xiàn)自己被綠了掘殴。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡粟誓,死狀恐怖奏寨,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情鹰服,我是刑警寧澤病瞳,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站悲酷,受9級特大地震影響套菜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜设易,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一逗柴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧顿肺,春花似錦戏溺、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至讼昆,卻和暖如春肋僧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工嫌吠, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人掺炭。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓辫诅,卻偏偏與公主長得像,于是被迫代替她去往敵國和親涧狮。 傳聞我的和親對象是個殘疾皇子炕矮,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,527評論 25 707
  • 如何安撫一顆向遠(yuǎn)方張望的心,唯有去遠(yuǎn)方者冤。 于是肤视,扶老攜幼也來到了西安。這一大家子人啊涉枫,和西安這座城市留給我的印象一...
    好好做教育的暖暖閱讀 214評論 0 0
  • 生活中邢滑,我們可以學(xué)到很多;學(xué)習(xí)中,我們可以學(xué)到很多;往事中愿汰,我們同樣可以學(xué)到很多困后。也許,稚嫩的我們還不理解生活的困...
    YolandaYanyoyo閱讀 198評論 0 0
  • 【今日打卡51】今天衬廷,我們單位的編輯找到我說我的文章有些內(nèi)容寫的太敏感了摇予。就是那個黨員同事前后對比的幫扶材料。作為...
    鄧男神Sweety閱讀 130評論 0 0