android設(shè)計(jì)模式之Builder模式

一、定義

講一個(gè)復(fù)雜對(duì)象的構(gòu)建與他的表示分離,使用同樣的構(gòu)建過(guò)程實(shí)現(xiàn)不同的表示;

  • 分類:創(chuàng)建型模式

二、使用場(chǎng)景

  1. 相同的方法,不同的執(zhí)行順序,產(chǎn)生不一樣的事件結(jié)果;

  2. 將多個(gè)零件(部分),通過(guò)不同的執(zhí)行順序,而產(chǎn)生不同表示結(jié)果;

  3. 產(chǎn)品類非常復(fù)雜,如:參數(shù)太多,很多參數(shù)都具有默認(rèn)值,或通過(guò)不同的順序,獲取不同的表示結(jié)果;

     核心:不同的方法順序,獲取不同的表示結(jié)果;
    

三、UML類圖

標(biāo)準(zhǔn)Builder模式
標(biāo)準(zhǔn)Builder模式
簡(jiǎn)單Builder模式
簡(jiǎn)單Builder模式
Builder的變種
Builder的變種

備注:Bean 存儲(chǔ)數(shù)據(jù)的實(shí)體,在實(shí)體內(nèi)部實(shí)現(xiàn)Builder;

角色介紹:
  • Product 產(chǎn)品(抽象)類
    可以是具體的產(chǎn)品,也可以是產(chǎn)品的抽象類,對(duì)產(chǎn)品的相關(guān)進(jìn)行設(shè)計(jì)或?qū)崿F(xiàn);
  • Buider抽象類
    用于編寫B(tài)uilder的抽象,規(guī)范產(chǎn)品的組建;
  • ConcreteBuilder實(shí)現(xiàn)類
    實(shí)現(xiàn)對(duì)產(chǎn)品的具體的操作流程;
  • Director(主管類)
    實(shí)現(xiàn)Builder的統(tǒng)一組裝過(guò)程;
    備注:一般實(shí)現(xiàn)過(guò)程中,經(jīng)常會(huì)將主管類省略,直接使用Builder對(duì)產(chǎn)品進(jìn)行操作,它會(huì)返回this對(duì)象,以此來(lái)實(shí)現(xiàn)Builder的鏈?zhǔn)秸{(diào)用,這種模式,我管他叫簡(jiǎn)單Builder模式;該方法,結(jié)構(gòu)更加簡(jiǎn)單,對(duì)比標(biāo)準(zhǔn)結(jié)構(gòu)也沒(méi)有明顯的缺點(diǎn),推薦使用這種模式;

四、代碼實(shí)現(xiàn)

代碼以顧客在蘋果專賣店挑選手機(jī)為例。顧客挑選手機(jī)時(shí),會(huì)根據(jù)系統(tǒng)、cpu、屏幕等等參數(shù)進(jìn)行選擇手機(jī);

1. 標(biāo)準(zhǔn)Builder模式

/**
 * Created by zhangxuehui on 2017/6/18.
 * 蘋果專賣店
 */
public class BuilderAppleShop {
    public static void main(String[] args) {

        Builder builder = new AppleBuilder();
        Director director = new Director(builder);
        director.buyMoble("A9", "4.7");
        System.out.println("用戶購(gòu)買的第一部手機(jī)是:" + buildr.create().toString());
        director.buyMoble("ios 11", "A10", "5.5");
        System.out.println("用戶購(gòu)買的第二部手機(jī)是:" +  buildr.create().toString());
    }


    /**
     * 手機(jī)配置抽象類,標(biāo)準(zhǔn)化手機(jī)的核心參數(shù)
     */
    static abstract class Mobile {
        protected String logo;//品牌
        protected String cpu;//cpu處理器
        protected String os;//系統(tǒng)
        protected String screenSize;//屏幕尺寸

        abstract void logo();//蘋果專賣店只有蘋果的產(chǎn)品,所以logo相當(dāng)于手機(jī)標(biāo)志,必須由具體的手機(jī)類實(shí)現(xiàn);

        public String getCpu() {
            return cpu;
        }

        public void setCpu(String cpu) {
            this.cpu = cpu;
        }

        public abstract void os();//手機(jī)一定會(huì)有系統(tǒng),需要有一個(gè)默認(rèn)的系統(tǒng),可以有用戶動(dòng)態(tài)設(shè)置

        public void setOs(String os) { //顧客可以選擇不同版本的系統(tǒng),
            this.os = os;
        }

        public String getScreenSize() {
            return screenSize;
        }

        public void setScreenSize(String screenSize) {
            this.screenSize = screenSize;
        }

        @Override
        public String toString() {
            return "Mobile{" +
                    "logo='" + logo + '\'' +
                    ", cpu='" + cpu + '\'' +
                    ", os='" + os + '\'' +
                    ", screenSize='" + screenSize + '\'' +
                    '}';
        }
    }

    /**
     * 蘋果手機(jī)類--產(chǎn)品的具體實(shí)現(xiàn)類
     */
    static class AppleMobile extends Mobile {

        @Override
        void logo() {
            logo = "apple";
        }

        @Override
        public void os() {
            os =  "ios 10";
        }

    }

    /**
     * 建造者的抽象類,規(guī)范操作以及流程
     */
    static abstract class Builder {
        abstract void builderLogo();

        abstract void builderOs();

        abstract void builderOs(String os);

        abstract void builderScreenSize(String size);

        abstract void builderCpu(String cpu);

        abstract Mobile create();
    }

    /**
     * 建造者的具體實(shí)現(xiàn)類
     */
    public static class AppleBuilder extends Builder {
        Mobile mobile = new AppleMobile();


        @Override
        void builderLogo() {
            mobile.logo();
        }

        @Override
        void builderOs() {
            mobile.os();
        }

        @Override
        void builderOs(String os) {
            mobile.setOs(os);
        }

        @Override
        void builderScreenSize(String size) {
            mobile.setScreenSize(size);
        }

        @Override
        void builderCpu(String cpu) {
            mobile.setCpu(cpu);
        }

        @Override
        Mobile create() {
            return mobile;
        }

    }

    /**
     * 主管類,對(duì)Builder進(jìn)行統(tǒng)一操作
     */
    public static class Director {
        Builder mBuildr = null;

        public Director(Builder mBuildr) {
            this.mBuildr = mBuildr;
            mBuildr.builderLogo();
        }

        //購(gòu)買自選系統(tǒng)版本的手機(jī)
        public void buyMoble(String os, String cpu, String size) {
            mBuildr.builderOs(os);
            mBuildr.builderCpu(cpu);
            mBuildr.builderScreenSize(size);
        }

        //購(gòu)買官方默認(rèn)系統(tǒng)的手機(jī)
        public void buyMoble(String cpu, String size) {
            mBuildr.builderCpu(cpu);
            mBuildr.builderScreenSize(size);
            mBuildr.builderOs();
        }
    }
}

備注:

  1. 引用產(chǎn)品的具體實(shí)現(xiàn)時(shí),實(shí)例的類型應(yīng)為其抽象類;
  2. Builder類中,一定要實(shí)現(xiàn)create()方法;
  3. 要對(duì)產(chǎn)品層、Builder層、Directer層,進(jìn)行合理劃分,Builder僅處理產(chǎn)品,Directer僅處理Builder;不可以出現(xiàn),Directer處理產(chǎn)品類的情況;

輸出結(jié)果

輸出結(jié)果

2. 簡(jiǎn)單Builder模式 (進(jìn)化版)


/**
 * Created by zhangxuehui on 2017/6/18.
 * 蘋果專賣店
 */
public class SimpleBuilderAppleShop {
    public static void main(String[] args) {

        Builder builder = new AppleBuilder();
        Mobile mobile1 = builder.builderScreenSize("5.5").builderCpu("A10").create();
        System.out.println("用戶購(gòu)買的第一部手機(jī)是:" + mobile1.toString());
        Mobile mobile2 = builder.builderScreenSize("4.7").builderCpu("A9").builderOs("ios 7").create();
        System.out.println("用戶購(gòu)買的第二部手機(jī)是:" + mobile2.toString());
    }


    /**
     * 手機(jī)配置抽象類,標(biāo)準(zhǔn)化手機(jī)的核心參數(shù)
     */
    static abstract class Mobile {
        protected String logo;//品牌
        protected String cpu;//cpu處理器
        protected String os;//系統(tǒng)
        protected String screenSize;//屏幕尺寸

        abstract void logo();//蘋果專賣店只有蘋果的產(chǎn)品,所以logo相當(dāng)于手機(jī)標(biāo)志,必須由具體的手機(jī)類實(shí)現(xiàn);

        public String getCpu() {
            return cpu;
        }

        public void setCpu(String cpu) {
            this.cpu = cpu;
        }

        public abstract void os();//手機(jī)一定會(huì)有系統(tǒng),需要有一個(gè)默認(rèn)的系統(tǒng),可以有用戶動(dòng)態(tài)設(shè)置

        public void setOs(String os) { //顧客可以選擇不同版本的系統(tǒng),
            this.os = os;
        }

        public String getScreenSize() {
            return screenSize;
        }

        public void setScreenSize(String screenSize) {
            this.screenSize = screenSize;
        }

        @Override
        public String toString() {
            return "Mobile{" +
                    "logo='" + logo + '\'' +
                    ", cpu='" + cpu + '\'' +
                    ", os='" + os + '\'' +
                    ", screenSize='" + screenSize + '\'' +
                    '}';
        }
    }

    /**
     * 蘋果手機(jī)類--產(chǎn)品的具體實(shí)現(xiàn)類
     */
    static class AppleMobile extends Mobile {

        @Override
        void logo() {
            logo = "apple";
        }

        @Override
        public void os() {
            os = "ios 10";
        }

    }

    /**
     * 建造者的抽象類,規(guī)范操作以及流程
     */
    static abstract class Builder {
        abstract Builder builderLogo();

        abstract Builder builderOs();

        abstract Builder builderOs(String os);

        abstract Builder builderScreenSize(String size);

        abstract Builder builderCpu(String cpu);

        abstract Mobile create();
    }

    /**
     * 建造者的具體實(shí)現(xiàn)類
     */
    public static class AppleBuilder extends Builder {
        Mobile mobile = new AppleMobile();

        //在構(gòu)造方法中初始化默認(rèn)值
        public AppleBuilder() {
            builderLogo();
            builderOs();
        }

        @Override
        Builder builderLogo() {
            mobile.logo();
            return this;//除去create()全部返回this,方便鏈?zhǔn)秸{(diào)用。
        }

        @Override
        Builder builderOs() {
            mobile.os();
            return this;
        }

        @Override
        Builder builderOs(String os) {
            mobile.setOs(os);
            return this;
        }

        @Override
        Builder builderScreenSize(String size) {
            mobile.setScreenSize(size);
            return this;
        }

        @Override
        Builder builderCpu(String cpu) {
            mobile.setCpu(cpu);
            return this;
        }

        @Override
        Mobile create() {
            return mobile;//將設(shè)置好的對(duì)象返回;
        }

    }

}

備注:

  1. 與標(biāo)準(zhǔn)模式的主要區(qū)別,在于完全去除了Direct類,用戶直接操作Builder;
  2. Builder類中,除create()其他全部返回this,便于鏈?zhǔn)秸{(diào)用;
  3. 用戶直接獲取最終配置好的產(chǎn)品類;

輸出結(jié)果

輸出結(jié)果

3. Builder的變種

public class User {
    private final String Name;//姓名  必選
    private final int idCode;//身份證  必選
    private final int age;//年齡  可選
    private final String like;//愛(ài)好 可選
    private final String language;//語(yǔ)言 可選
    private final String address;//地址 可選

    //傳人Builder對(duì)實(shí)體設(shè)置參數(shù)
    public User(Builder builder) {
        this.Name = builder.name;
        this.idCode = builder.idCode;
        this.age = builder.age;
        this.like = builder.like;
        this.language = builder.language;
        this.address = builder.address;
    }

    @Override
    public String toString() {
        return "User{" +
                "Name='" + Name + '\'' +
                ", idCode=" + idCode +
                ", age=" + age +
                ", like='" + like + '\'' +
                ", language='" + language + '\'' +
                ", address='" + address + '\'' +
                '}';
    }

    /**
     * 實(shí)現(xiàn)builder內(nèi)部類,用于配置實(shí)體的參數(shù)
     */
    public static class Builder {

        //拷貝一份與user實(shí)體相同的參數(shù),并且不設(shè)置為final
        private String name;//姓名  必選
        private int idCode;//身份證  必選
        private int age;//年齡  可選
        private String like;//愛(ài)好 可選
        private String language;//語(yǔ)言 可選
        private String address;//地址 可選

        //對(duì)必選的參數(shù)在構(gòu)造方法中進(jìn)行設(shè)置
        public Builder(String name, int idCode) {
            this.name = name;
            this.idCode = idCode;
        }

        //參數(shù)設(shè)置時(shí),應(yīng)該返回當(dāng)前類對(duì)象,用于鏈?zhǔn)秸{(diào)用
        public Builder setAge(int age) {
            this.age = age;
            return this;
        }

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

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

        public Builder setAddress(String address) {
            this.address = address;
            return this;
        }
        //配置完參數(shù)后,將builder傳遞給User,完成創(chuàng)建。
        protected User create() {
            return new User(this);
        }
    }
}

使用

public static void main(String[] args) {
        User u = new User.Builder("張山",123)
                .setAddress("北京市")
                .setAge(18)
                .setLanguage("中文")
                .setLike("游泳")
                .create();

        System.out.println(u.toString());
}

備注
1.該模式適用于,需要的初始化操作,或構(gòu)造方法的時(shí)候需要傳人大量的參數(shù);

五、android源碼中的實(shí)現(xiàn)(Dialog)


public class AlertDialog extends AppCompatDialog implements DialogInterface {

    final AlertController mAlert;//Dialog的控制類,所有邏輯都在其內(nèi)部實(shí)現(xiàn)

    /********無(wú)關(guān)代碼忽略********/

    /**
     * Construct an AlertDialog that uses an explicit theme.  The actual style
     * that an AlertDialog uses is a private implementation, however you can
     * here supply either the name of an attribute in the theme from which
     * to get the dialog's style (such as {@link R.attr#alertDialogTheme}.
     * 核心構(gòu)造方法,用于綁定AlertController
     */
    protected AlertDialog(@NonNull Context context, @StyleRes int themeResId) {
        super(context, resolveDialogTheme(context, themeResId));
        mAlert = new AlertController(getContext(), this, getWindow());
    }

   //對(duì)外開(kāi)放的按鈕, 
   public Button getButton(int whichButton) {
        return mAlert.getButton(whichButton);
    }

   /*這一部分,是對(duì)外開(kāi)放的view操作,如設(shè)置按鈕、圖標(biāo)、文字等*/
    

    @Override
    protected void onCreate(Bundle savedInstanceState) {//與生命周期綁定
        super.onCreate(savedInstanceState);
        mAlert.installContent();
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {//對(duì)按鍵進(jìn)行處理
        if (mAlert.onKeyDown(keyCode, event)) {
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }

    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {//對(duì)按鍵進(jìn)行處理
        if (mAlert.onKeyUp(keyCode, event)) {
            return true;
        }
        return super.onKeyUp(keyCode, event);
    }

    /**
      * 重點(diǎn)來(lái)啦,AlertDialog的builder類
      **/
    public static class Builder {
        //核心參數(shù),在Builder中配置的參數(shù)都會(huì)賦值給它,再由它轉(zhuǎn)交給AlertController,最后由AlertDialog,直接獲取Controller;
        private final AlertController.AlertParams P;
        private final int mTheme;

        /**
         * 構(gòu)造,必填參數(shù)context,略
         */
        public Builder(@NonNull Context context) {
            this(context, resolveDialogTheme(context, 0));
        }

         /**
         * 構(gòu)造,必填參數(shù)context和Theme,略
         */
        public Builder(@NonNull Context context, @StyleRes int themeResId) {
            P = new AlertController.AlertParams(new ContextThemeWrapper(
                    context, resolveDialogTheme(context, themeResId)));
            mTheme = themeResId;
        }

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

        /**
         * 這一部分為,配置標(biāo)題、圖片、文字、listview、監(jiān)聽(tīng)事件等,所有的配置參數(shù)都會(huì)通過(guò)P配置,如:P.mTitle = P.mContext.getText(titleId),其他方法基本與上邊相同,不解釋;
         */

        /**
         * 重點(diǎn)看看它的create實(shí)現(xiàn)。
         * 1.初始化AlertDialog;
         * 2.將AlertController.AlertParams中的參數(shù)配置給AlertDialog。
         * 3.配置監(jiān)聽(tīng)事件,并返回AlertDialog對(duì)象
         */
        public AlertDialog create() {
            // We can't use Dialog's 3-arg constructor with the createThemeContextWrapper param,
            // so we always have to re-set the theme
            final AlertDialog dialog = new AlertDialog(P.mContext, mTheme);
            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;
        }

        /**
         * 用于顯示dialog
         */
        public AlertDialog show() {
            final AlertDialog dialog = create();
            dialog.show();
            return dialog;
        }
    }

}

總結(jié)
源碼中的Builder使用的很有趣,他沒(méi)有像我們常見(jiàn)的講參數(shù)直接配置給對(duì)象,而是通過(guò)一個(gè)Controller類實(shí)現(xiàn)邏輯,Controller的內(nèi)部類AlertParams配置它的參數(shù),而不論Builder類還是AlertDialog類都只是一個(gè)入口。當(dāng)然一切都從Builder開(kāi)始。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容