android设计模式之Builder模式

一、定义

讲一个复杂对象的构建与他的表示分离,使用同样的构建过程实现不同的表示;

  • 分类:创建型模式

二、使用场景

  1. 相同的方法,不同的执行顺序,产生不一样的事件结果;

  2. 将多个零件(部分),通过不同的执行顺序,而产生不同表示结果;

  3. 产品类非常复杂,如:参数太多,很多参数都具有默认值,或通过不同的顺序,获取不同的表示结果;

     核心:不同的方法顺序,获取不同的表示结果;
    

三、UML类图

标准Builder模式
标准Builder模式
简单Builder模式
简单Builder模式
Builder的变种
Builder的变种

备注:Bean 存储数据的实体,在实体内部实现Builder;

角色介绍:
  • Product 产品(抽象)类
    可以是具体的产品,也可以是产品的抽象类,对产品的相关进行设计或实现;
  • Buider抽象类
    用于编写Builder的抽象,规范产品的组建;
  • ConcreteBuilder实现类
    实现对产品的具体的操作流程;
  • Director(主管类)
    实现Builder的统一组装过程;
    备注:一般实现过程中,经常会将主管类省略,直接使用Builder对产品进行操作,它会返回this对象,以此来实现Builder的链式调用,这种模式,我管他叫简单Builder模式;该方法,结构更加简单,对比标准结构也没有明显的缺点,推荐使用这种模式;

四、代码实现

代码以顾客在苹果专卖店挑选手机为例。顾客挑选手机时,会根据系统、cpu、屏幕等等参数进行选择手机;

1. 标准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("用户购买的第一部手机是:" + buildr.create().toString());
        director.buyMoble("ios 11", "A10", "5.5");
        System.out.println("用户购买的第二部手机是:" +  buildr.create().toString());
    }


    /**
     * 手机配置抽象类,标准化手机的核心参数
     */
    static abstract class Mobile {
        protected String logo;//品牌
        protected String cpu;//cpu处理器
        protected String os;//系统
        protected String screenSize;//屏幕尺寸

        abstract void logo();//苹果专卖店只有苹果的产品,所以logo相当于手机标志,必须由具体的手机类实现;

        public String getCpu() {
            return cpu;
        }

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

        public abstract void os();//手机一定会有系统,需要有一个默认的系统,可以有用户动态设置

        public void setOs(String os) { //顾客可以选择不同版本的系统,
            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 + '\'' +
                    '}';
        }
    }

    /**
     * 苹果手机类--产品的具体实现类
     */
    static class AppleMobile extends Mobile {

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

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

    }

    /**
     * 建造者的抽象类,规范操作以及流程
     */
    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();
    }

    /**
     * 建造者的具体实现类
     */
    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;
        }

    }

    /**
     * 主管类,对Builder进行统一操作
     */
    public static class Director {
        Builder mBuildr = null;

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

        //购买自选系统版本的手机
        public void buyMoble(String os, String cpu, String size) {
            mBuildr.builderOs(os);
            mBuildr.builderCpu(cpu);
            mBuildr.builderScreenSize(size);
        }

        //购买官方默认系统的手机
        public void buyMoble(String cpu, String size) {
            mBuildr.builderCpu(cpu);
            mBuildr.builderScreenSize(size);
            mBuildr.builderOs();
        }
    }
}

备注:

  1. 引用产品的具体实现时,实例的类型应为其抽象类;
  2. Builder类中,一定要实现create()方法;
  3. 要对产品层、Builder层、Directer层,进行合理划分,Builder仅处理产品,Directer仅处理Builder;不可以出现,Directer处理产品类的情况;

输出结果

输出结果

2. 简单Builder模式 (进化版)


/**
 * 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("用户购买的第一部手机是:" + mobile1.toString());
        Mobile mobile2 = builder.builderScreenSize("4.7").builderCpu("A9").builderOs("ios 7").create();
        System.out.println("用户购买的第二部手机是:" + mobile2.toString());
    }


    /**
     * 手机配置抽象类,标准化手机的核心参数
     */
    static abstract class Mobile {
        protected String logo;//品牌
        protected String cpu;//cpu处理器
        protected String os;//系统
        protected String screenSize;//屏幕尺寸

        abstract void logo();//苹果专卖店只有苹果的产品,所以logo相当于手机标志,必须由具体的手机类实现;

        public String getCpu() {
            return cpu;
        }

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

        public abstract void os();//手机一定会有系统,需要有一个默认的系统,可以有用户动态设置

        public void setOs(String os) { //顾客可以选择不同版本的系统,
            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 + '\'' +
                    '}';
        }
    }

    /**
     * 苹果手机类--产品的具体实现类
     */
    static class AppleMobile extends Mobile {

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

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

    }

    /**
     * 建造者的抽象类,规范操作以及流程
     */
    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();
    }

    /**
     * 建造者的具体实现类
     */
    public static class AppleBuilder extends Builder {
        Mobile mobile = new AppleMobile();

        //在构造方法中初始化默认值
        public AppleBuilder() {
            builderLogo();
            builderOs();
        }

        @Override
        Builder builderLogo() {
            mobile.logo();
            return this;//除去create()全部返回this,方便链式调用。
        }

        @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;//将设置好的对象返回;
        }

    }

}

备注:

  1. 与标准模式的主要区别,在于完全去除了Direct类,用户直接操作Builder;
  2. Builder类中,除create()其他全部返回this,便于链式调用;
  3. 用户直接获取最终配置好的产品类;

输出结果

输出结果

3. Builder的变种

public class User {
    private final String Name;//姓名  必选
    private final int idCode;//身份证  必选
    private final int age;//年龄  可选
    private final String like;//爱好 可选
    private final String language;//语言 可选
    private final String address;//地址 可选

    //传人Builder对实体设置参数
    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 + '\'' +
                '}';
    }

    /**
     * 实现builder内部类,用于配置实体的参数
     */
    public static class Builder {

        //拷贝一份与user实体相同的参数,并且不设置为final
        private String name;//姓名  必选
        private int idCode;//身份证  必选
        private int age;//年龄  可选
        private String like;//爱好 可选
        private String language;//语言 可选
        private String address;//地址 可选

        //对必选的参数在构造方法中进行设置
        public Builder(String name, int idCode) {
            this.name = name;
            this.idCode = idCode;
        }

        //参数设置时,应该返回当前类对象,用于链式调用
        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;
        }
        //配置完参数后,将builder传递给User,完成创建。
        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.该模式适用于,需要的初始化操作,或构造方法的时候需要传人大量的参数;

五、android源码中的实现(Dialog)


public class AlertDialog extends AppCompatDialog implements DialogInterface {

    final AlertController mAlert;//Dialog的控制类,所有逻辑都在其内部实现

    /********无关代码忽略********/

    /**
     * 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}.
     * 核心构造方法,用于绑定AlertController
     */
    protected AlertDialog(@NonNull Context context, @StyleRes int themeResId) {
        super(context, resolveDialogTheme(context, themeResId));
        mAlert = new AlertController(getContext(), this, getWindow());
    }

   //对外开放的按钮, 
   public Button getButton(int whichButton) {
        return mAlert.getButton(whichButton);
    }

   /*这一部分,是对外开放的view操作,如设置按钮、图标、文字等*/
    

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

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {//对按键进行处理
        if (mAlert.onKeyDown(keyCode, event)) {
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }

    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {//对按键进行处理
        if (mAlert.onKeyUp(keyCode, event)) {
            return true;
        }
        return super.onKeyUp(keyCode, event);
    }

    /**
      * 重点来啦,AlertDialog的builder类
      **/
    public static class Builder {
        //核心参数,在Builder中配置的参数都会赋值给它,再由它转交给AlertController,最后由AlertDialog,直接获取Controller;
        private final AlertController.AlertParams P;
        private final int mTheme;

        /**
         * 构造,必填参数context,略
         */
        public Builder(@NonNull Context context) {
            this(context, resolveDialogTheme(context, 0));
        }

         /**
         * 构造,必填参数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;
        }

        /**
         * 这一部分为,配置标题、图片、文字、listview、监听事件等,所有的配置参数都会通过P配置,如:P.mTitle = P.mContext.getText(titleId),其他方法基本与上边相同,不解释;
         */

        /**
         * 重点看看它的create实现。
         * 1.初始化AlertDialog;
         * 2.将AlertController.AlertParams中的参数配置给AlertDialog。
         * 3.配置监听事件,并返回AlertDialog对象
         */
        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;
        }
    }

}

总结
源码中的Builder使用的很有趣,他没有像我们常见的讲参数直接配置给对象,而是通过一个Controller类实现逻辑,Controller的内部类AlertParams配置它的参数,而不论Builder类还是AlertDialog类都只是一个入口。当然一切都从Builder开始。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容