设计模式之二:Builder模式

来源:互联网 发布:淘宝卖家售假被扣48分 编辑:程序博客网 时间:2024/06/05 05:21

设计模式之二:Builder模式
目录介绍
0.关于Builder模式案例下载
1.Builder模式介绍
2.Builder模式使用场景
3.Builder模式简单案例
3.1 Builder模式UML图(摘自网络)
3.2 在《Android源码设计模式》这本书上,介绍经典Builder模式中,包括
3.3 Product角色
3.4 Builder : 抽象Builder类,规范产品组建,一般是由子类实现具体的组建过程
3.5 ConcreteBuilder : 具体的Builder类
3.6 Director : 统一组装过程
4.Builder模式实际案例Demo
4.1 实际开发中,有时会遇到复杂的对象的代码
4.2 通过构造函数的参数形式去写一个实现类,或者通过set,get去实现
4.3 分析
4.4 将上面例子改成builder模式如下所示
4.5 最后运用,代码如下
4.6 关于线程安全问题
5.Android源码中的Builder模式实现
5.1 首先看看AlertDialog.Builder源代码,只是摘自部分代码
5.2 接下来看看build源代码
5.3 接下来看看show的代码
5.4 为什么AlertDialog要使用builder模式呢?
6.Builder模式总结
6.1 builder模式优点
6.2 builder模式缺点
7.关于其他说明
7.1 参考案例
8.关于其他更多内容

0.本人写的综合案例
案例
说明及截图
模块:新闻,音乐,视频,图片,唐诗宋词,快递,天气,记事本,阅读器等等
接口:七牛,阿里云,天行,干货集中营,极速数据,追书神器等等

关于Builder设计模式,具体实际案例可以参考:https://github.com/yangchong211/YCDialog

1.Builder模式介绍

1.1 定义: 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的展示。
1.2 Builder模式属于创建型,一步一步将一个复杂对象创建出来,允许用户在不知道内部构建细节的情况下,可以更精细地控制对象的构造流程。

2.Builder模式使用场景

相同方法,不同执行顺序,产生不同事件结果
初始化对象特别复杂,参数众多,且很多参数都具有默认值时

3.Builder模式简单案例

3.1 Builder模式UML图(摘自网络)
- Image.png

3.2 在《Android源码设计模式》这本书上,介绍经典Builder模式中,包括
- Product : 产品抽象类
- Builder : 抽象Builder类,规范产品组建,一般是由子类实现具体的组建过程
- ConcreteBuilder : 具体的Builder类
- Director : 统一组装过程

3.3 Product角色

//设置抽象类public abstract class BuilderUser {    protected String name;    protected String cardID;    protected int age;    protected String address;    public BuilderUser() {}    //设置名字    public void setName(String name) {        this.name = name;    }    //抽象方法    public abstract void setCardID();    //设置年龄    public void setAge(int age) {        this.age = age;    }    //设置地址    public void setAddress(String address) {        this.address = address;    }    @Override    public String toString() {        return "BuilderUser{" +                "name='" + name + '\'' +                ", cardID='" + cardID + '\'' +                ", age=" + age +                ", address='" + address + '\'' +                '}';    }}/** * 具体的Product角色 UserCard */public class UserCard extends BuilderUser {    public UserCard() {}    @Override    public void setCardID() {        cardID="10086";             //设置默认ID    }}

3.4 Builder : 抽象Builder类,规范产品组建,一般是由子类实现具体的组建过程

/** * 抽象Builder类 */public abstract class Builder {    public abstract void buildName(String name);    public abstract void buildCardID();    public abstract void buildAge(int age);    public abstract void buildAddress(String address);    public abstract BuilderUser create();}

3.5 ConcreteBuilder : 具体的Builder类

/*** 具体的Builder类*/public class AccountBuilder extends Builder{    private BuilderUser user = new UserCard();    @Override    public void buildName(String name) {        user.setName(name);    }    @Override    public void buildCardID() {        user.setCardID();    }    @Override    public void buildAge(int age) {        user.setAge(age);    }    @Override    public void buildAddress(String address) {        user.setAddress(address);    }    @Override    public BuilderUser create() {        return user;    }}

3.6 Director : 统一组装过程

/*** Director角色,负责构造User*/public class Director {    Builder mBuilder =null;    public Director(Builder builder){        this.mBuilder =builder;    }    public void construct(String name,int age,String address){        mBuilder.buildName(name);        mBuilder.buildCardID();        mBuilder.buildAge(age);        mBuilder.buildAddress(address);    }}

4.Builder模式实际案例Demo

4.1 实际开发中,有时会遇到复杂的对象的代码,比如

public class BuilderDemo {    private final String name;         //必选    private final String cardID;       //必选    private final int age;             //可选    private final String address;      //可选    private final String phone;        //可选}

4.2 通过构造函数的参数形式去写一个实现类,或者通过set,get去实现

BuilderDemo(String name);BuilderDemo(String name, String cardID);BuilderDemo(String name, String cardID,int age);BuilderDemo(String name, String cardID,int age, String address);BuilderDemo(String name, String cardID,int age, String address,String phone);

4.3 分析
- 第一种在参数不多的情况下,是比较方便快捷的,一旦参数多了,代码可读性大大降低,并且难以维护,对调用者来说也造成一定困惑;
- 第二种可读性不错,也易于维护,但是这样子做对象会产生不一致的状态,当你想要传入全部参数的时候,你必需将所有的setXX方法调用完成之后才行。然而一部分的调用者看到了这个对象后,以为这个对象已经创建完毕,就直接使用了,其实User对象并没有创建完成,另外,这个User对象也是可变的,不可变类所有好处都不复存在。
- Builder模式同时满足上面两种情况,易于维护,并且创建方便

4.4 将上面例子改成builder模式如下所示

public class BuilderDemo {    private final String name;         //必选    private final String cardID;       //必选    private final int age;             //可选    private final String address;      //可选    private final String phone;        //可选    public BuilderDemo(UserBuilder userBuilder){        this.name=userBuilder.name;        this.cardID=userBuilder.cardID;        this.age=userBuilder.age;        this.address=userBuilder.address;        this.phone=userBuilder.phone;    }    public static class UserBuilder{        private final String name;        private final String cardID;        private int age;        private String address;        private String phone;        public BuilderDemo build(){            return new BuilderDemo(this);        }        public UserBuilder(String name,String cardID){            this.name=name;            this.cardID=cardID;        }        public UserBuilder age(int age){            this.age=age;            return this;        }        public UserBuilder address(String address){            this.address=address;            return this;        }        public UserBuilder phone(String phone){            this.phone=phone;            return this;        }    }}

4.5 最后运用,代码如下

new BuilderDemo.UserBuilder("yc","10086")        .age(24)        .address("beijing")        .phone("13667225184")        .build();

4.6 关于线程安全问题

// 线程安全public BuilderDemo build(){    BuilderDemo builderDemo = new BuilderDemo(this);    if(builderDemo.age > 100){        throw new IllegalStateException("Age out of range");    }    return builderDemo;}// 线程不安全,不要这样写public BuilderDemo build(){    if(age > 100){        throw new IllegalStateException("Age out of range");    }    return new BuilderDemo(this);}

5.Android源码中的Builder模式实现

5.1 首先看看AlertDialog.Builder源代码,只是摘自部分代码

public class AlertDialog extends AppCompatDialog implements DialogInterface {    //接收builder成员变量p中各个参数    final AlertController mAlert;    //下面是三个构造函数    protected AlertDialog(@NonNull Context context) {        this(context, 0);    }    protected AlertDialog(@NonNull Context context, @StyleRes int themeResId) {        super(context, resolveDialogTheme(context, themeResId));        mAlert = new AlertController(getContext(), this, getWindow());    }    protected AlertDialog(@NonNull Context context, boolean cancelable,            @Nullable OnCancelListener cancelListener) {        this(context, 0);        setCancelable(cancelable);        setOnCancelListener(cancelListener);    }    //事件上这里调用了是mAlert的setView方法    public void setView(View view) {        mAlert.setView(view);    }    public void setView(View view, int viewSpacingLeft, int viewSpacingTop, int viewSpacingRight,            int viewSpacingBottom) {        mAlert.setView(view, viewSpacingLeft, viewSpacingTop, viewSpacingRight, viewSpacingBottom);    }    public static class Builder {        //这个里面存放很多参数        private final AlertController.AlertParams P;        //这个是new AlertDialog.Builder(context,R.style.AppTheme)调用的方法        public Builder(@NonNull Context context, @StyleRes int themeResId) {            P = new AlertController.AlertParams(new ContextThemeWrapper(                    context, resolveDialogTheme(context, themeResId)));            mTheme = themeResId;        }        //这部分代码省略很多,主要是set设置各种参数        public Builder setTitle(@Nullable CharSequence title) {            P.mTitle = title;            return this;        }              //这部分代码省略很多,主要是set设置各种参数        public Builder setView(int layoutResId) {            P.mView = null;            P.mViewLayoutResId = layoutResId;            P.mViewSpacingSpecified = false;            return this;        }        //构建dialog,传递参数        public AlertDialog create() {            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;        }    }}

5.2 接下来看看build源代码
- 其中P是一个保存设置AlterDialog的参数类AlertParams的对象。很显然,Builder只是将AlterDialog的参数存放在一个参数对象里,做一个暂时的保存。在最后调用create方法的时候,创建一个AlterDialog对象,将暂存的参数P一次性应用到这个Dialog对象上。完成Dialog的创建。
当然,这只是创建AlertDialog,还没show出来。可以直接调用刚创建的AlertDialog的show()方法(实际为调用父类Dialog的show()),也可以在构建完Builder之后直接调用Builder的show方法

public AlertDialog create() {    //创建    final AlertDialog dialog = new AlertDialog(P.mContext, mTheme);    //这一步很重要,下面会单独分析    P.apply(dialog.mAlert);    //设置是否可以取消,默认是true    dialog.setCancelable(P.mCancelable);    if (P.mCancelable) {        dialog.setCanceledOnTouchOutside(true);    }    //设置取消监听    dialog.setOnCancelListener(P.mOnCancelListener);    //设置dialog关闭后监听    dialog.setOnDismissListener(P.mOnDismissListener);    //设置返回键监听    if (P.mOnKeyListener != null) {        dialog.setOnKeyListener(P.mOnKeyListener);    }    //返回对象,创建成功    return dialog;}

5.3 接下来看看show的代码

public void show() {    //如果弹窗已经show出来了     if (mShowing) {        //如果顶级view存在则设置窗口window,并显示顶级View         if (mDecor != null) {            if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {                mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR);            }            mDecor.setVisibility(View.VISIBLE);        }        return;    }    mCanceled = false;    //如果窗口还未被创建     if (!mCreated) {        dispatchOnCreate(null);    }    //获得Windowd的顶级View,并将其添加到对应的WindowManager中,然后发送show的消息     onStart();    //获取DecorView    mDecor = mWindow.getDecorView();    if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {        final ApplicationInfo info = mContext.getApplicationInfo();        mWindow.setDefaultIcon(info.icon);        mWindow.setDefaultLogo(info.logo);        mActionBar = new WindowDecorActionBar(this);    }    //获取布局参数    WindowManager.LayoutParams l = mWindow.getAttributes();    if ((l.softInputMode            & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {        WindowManager.LayoutParams nl = new WindowManager.LayoutParams();        nl.copyFrom(l);        nl.softInputMode |=                WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;        l = nl;    }    try {        //将mDecor添加到WindowManager中        mWindowManager.addView(mDecor, l);        mShowing = true;        //发送一个显示Dialog消息        sendShowMessage();    } finally {    }}
  • 在show函数中主要做了如下的事情:
    (1)通过dispatchOnCreate函数来调用AlertDialog的onCreate函数
    (2)然后接着调用onStart函数
    (3)最后将Dialog的mDecor添加到WindowManager中(有点不明白这里,为什么要获取到最顶部的布局)

5.4 为什么AlertDialog要使用builder模式呢?
- AlterDialog有很多的参数,如我们上面列举的title,message,三个button及其回调,除此以外还有icon,View等属性。如果Android不采用Builder,而采用普通的构造函数来设计AlterDialog,可能要写出很多类似于如下的构造函数:

AlertDialog(String title);AlertDialog(String message)AlertDialog(int resId)AlertDialog(int resId, String title, String message);AlterDialog(int resId, String title, String message, String PositiveButtonString, OnClickListener listener);AlterDialog(int resId, String title, String message, String PositiveButtonString, OnClickListener listener);AlterDialog(int resId, String title, String message, String NegativeButtonString, OnClickListener listener);AlterDialog(int resId, String title, String message, String PositiveButtonString, OnClickListener listener, String NegativeButtonString, OnClickListener listener);....

6.Builder模式总结

6.1 Builder模式有几个好处:
1. Builder的setter函数可以包含安全检查,可以确保构建过程的安全,有效。
2. Builder的setter函数是具名函数,有意义,相对于构造函数的一长串函数列表,更容易阅读维护。
3. 可以利用单个Builder对象构建多个对象,Builder的参数可以在创建对象的时候利用setter函数进行调整

6.2 当然Builder模式也有缺点:
1. 更多的代码,需要Builder这样的内部类
2. 增加了类创建的运行时开销 ,但是当一个类参数很多的时候,Builder模式带来的好处要远胜于其缺点。

7.关于其他说明

7.1 参考案例
- 参考《Android源码设计解析与实战 》

8.关于其他更多内容

  • github:https://github.com/yangchong211
  • 知乎:https://www.zhihu.com/people/yang-chong-69-24/pins/posts
  • 领英:https://www.linkedin.com/in/chong-yang-049216146/
  • 简书:http://www.jianshu.com/u/b7b2c6ed9284
  • csdn:http://my.csdn.net/m0_37700275
  • 网易博客:http://yangchong211.blog.163.com/
  • 新浪博客:http://blog.sina.com.cn/786041010yc
  • 喜马拉雅听书:http://www.ximalaya.com/zhubo/71989305/
  • 脉脉:yc930211
  • 360图书馆:http://www.360doc.com/myfiles.aspx
  • 开源中国:https://my.oschina.net/zbj1618/blog
  • 泡在网上的日子:http://www.jcodecraeer.com/member/content_list.php?channelid=1
  • 邮箱:yangchong211@163.com
  • 阿里云博客:https://yq.aliyun.com/users/article?spm=5176.100239.headeruserinfo.3.dT4bcV
  • 博客园:http://www.cnblogs.com/yc211/
原创粉丝点击