文章首发于个人博客

参考:

  • 经典 Builder / 变种 Builder 模式及自动化生成代码插件
  • 变种 Builder 模式:优雅的对象构建方式
  • 《Android源码设计模式解析与实战》
  • 重学设计模式 -- 建造者模式
  • 王者荣耀之「建造者模式」
  • 结合 Android 浅谈 Builder 模式
  • 人人都会设计模式---建造者模式--Builder

前言

builder模式也叫建造者模式, 也是Android中应用十分广泛的设计模式之一. 举个例子, 比如我们经常使用的框架okhttp和retrofit

OkHttpClient client = new Builder().writeTimeout(10, TimeUnit.SECONDS)        .readTimeout(10, TimeUnit.SECONDS)        .connectTimeout(10, TimeUnit.SECONDS)        .addInterceptor(interceptor)        .cache(cache)        .build();复制代码
Retrofit retrofit = new Retrofit.Builder().baseUrl(BASE_URL)        .addConverterFactory(GsonConverterFactory.create())        .addCallAdapterFactory(RxJavaCallAdapterFactory.create())        .client(okhttpClient)        .build();复制代码

还有Android中AlerDialog

new AlertDialog.Builder(this).setTitle("这是标题")        .setMessage("这是Message")        .setPositiveButton("确定", new DialogInterface.OnClickListener() {            @Override            public void onClick(DialogInterface dialog, int which) {            }        })        .setNegativeButton("取消", new DialogInterface.OnClickListener() {            @Override            public void onClick(DialogInterface dialog, int which) {            }        })        .create()        .show();复制代码

所以我们赶快去探究一下这神器的builder模式吧

Builder模式介绍

该模式是为了将构建复杂对象的过程和它的部件解耦, 构建过程和部件都可以自由扩展, 降低耦合.举例来说, 如果一个类有很多种构造方法, 或者一个构造方法中需要传入很多的参数, 比如说需要十几个参数, 可想而知这样的构造函数是十分容易出错的.而如果使用set方法来依次传入参数, 又失去了链式调用的优雅性, 代码变得不连续.这个时候使用builder模式就非常适合了.

Builder模式的UML类图

这时候又要再次科普一下UML类图中各种图形的含义了(因为我每次都容易忘记). 每个矩形都代表一个类(包括class和interface), 比如中间的那个Builder, 第一层显示它的类名是Builder, 顶端的`《abstract》`表示这是一个抽象类, 第二层表示它有三个方法, '+'表示方法是public的, 三个方法的返回类型都是void. 空心三角形 + 实线 表示ConcreteBuilder是继承自Builder的 箭头 + 虚线 表示依赖关系, 箭头指向被使用者, 比如这里的意思就是说ConcreteBuilder依赖Product, 因为ConcreteBuilder在组装的时候还是需要一个Product的, 不然它把零件组装到哪里去呢?也就是说ConcreteBuilder持有Product的引用. 空心菱形 + 实线(或者箭头线) 表示聚合关系, 汽车对象由轮胎对象聚合而成,但是轮胎对象的生命期并不受汽车对象的左右。当汽车对象销毁时,轮胎对象也可以单独存在.在这里, Director中必定有一个变量能够指向Builder, 表示聚合关系.

经典的Builder模式

Product

/** * 计算机抽象类, 即Product角色 */public abstract class Computer {    protected String mBoard;    protected String mDisplay;    protected String mOS;    public Computer() {    }    public void setBoard(String board) {        mBoard = board;    }    public void setDisplay(String display) {        mDisplay = display;    }    public abstract void setOS();    @Override    public String toString() {        return "Computer{"                + "mBoard='"                + mBoard                + '\''                + ", mDisplay='"                + mDisplay                + '\''                + ", mOS='"                + mOS                + '\''                + '}';    }}复制代码

具体的Product

/** * 具体的Computer类 */public class Macbook extends Computer {    public Macbook() {    }    @Override    public void setOS() {        mOS = "Mac OS X 10.10";    }}复制代码

抽象Builder

/** * 抽象Builder类 */public abstract class Builder {    public abstract void buildBoard(String board);    public abstract void buildDisplay(String display);    public abstract void buildOS();    public abstract Computer create();}复制代码

ConcreteBuilder

/** * 具体的Builder类, MacbookBuilder */public class MacbookBuilder extends Builder {    private Computer mComputer = new Macbook();    @Override    public void buildBoard(String board) {        mComputer.setBoard(board);    }    @Override    public void buildDisplay(String display) {        mComputer.setDisplay(display);    }    @Override    public void buildOS() {        mComputer.setOS();    }    @Override    public Computer create() {        return mComputer;    }}复制代码

Director

/** * Director类, 负责构造Computer */public class Director {    Builder mBuilder;    public Director(Builder builder) {        mBuilder = builder;    }    public void construct(String board, String display) {        mBuilder.buildBoard(board);        mBuilder.buildDisplay(display);        mBuilder.buildOS();    }}复制代码

测试代码

public class Main {    public static void main(String[] args) {        Builder builder = new MacbookBuilder();        Director pcDirector = new Director(builder);        pcDirector.construct("英特尔主板", "Retina 显示器");        Computer macBook = builder.create();        System.out.println("Computer Info: " + macBook.toString());    }}复制代码

输出结果:

Computer Info: Computer{mBoard='英特尔主板', mDisplay='Retina 显示器', mOS='Mac OS X 10.10'}复制代码

可以看出, 经典的 Builder 模式重点在于抽象出对象创建的步骤,并通过调用不同的具体实现类从而得到不同的结果, 但是在创建过程中依然要传入多个参数, 不是很方便, 所以有了变种的Builder模式

变种的Builder模式

目前来说在 Android&Java 开发过程中经典的 Builder 模式使用的较少,一般广泛使用的是他的一个变种
在日常的开发中 Director 角色经常会被忽略,这样会相对的减少了构造的步骤而直接使用一个 Builder 来进行对象的组装
这里我要说的就是一种内部Builder并且能够链式调用的变种
我们就直接进入实战, 模仿AlertDialog写一个dialogfragment, 但是比AlertDialog更简单些.

/** * Created by mundane on 2018/3/10 下午6:03 */public class EasyDialogFragment extends DialogFragment implements OnClickListener {    private static final String KEY_TITLE = "key_title";    private String mTitle;    private TextView mTvTitle;    private View mBtnCancel;    private View mBtnConfirm;    interface OnClickListener {        void onClick();    }    private OnClickListener mPositiveListener;    private OnClickListener mNegativeListener;    private void setPositiveListener(OnClickListener onClickListener) {        mPositiveListener = onClickListener;    }    private void setNegativeListener(OnClickListener onClickListener) {        mNegativeListener = onClickListener;    }    @Override    public void onClick(View v) {        switch (v.getId()) {            case R.id.btn_cancel:                if (mNegativeListener != null) {                    mNegativeListener.onClick();                }                break;            case R.id.btn_confirm:                if (mPositiveListener != null) {                    mPositiveListener.onClick();                }                break;        }        dismiss();    }    public static final class Builder {        private String title;        private OnClickListener mPositiveListener;        private OnClickListener mNegativeListener;        public Builder() {            title = "";        }        public Builder setTitle(String title) {            this.title = title;            return this;        }        public Builder setPositiveButton(OnClickListener onClickListener) {            mPositiveListener = onClickListener;            return this;        }        public Builder setNegativeButton(OnClickListener onClickListener) {            mNegativeListener = onClickListener;            return this;        }        public DialogFragment build() {            EasyDialogFragment dialogFragment = new EasyDialogFragment();            Bundle bundle = new Bundle();            bundle.putString(KEY_TITLE, title);            dialogFragment.setArguments(bundle);            dialogFragment.setPositiveListener(mPositiveListener);            dialogFragment.setNegativeListener(mNegativeListener);            return dialogFragment;        }    }    @Override    public void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        Bundle bundle = getArguments();        mTitle = bundle.getString(KEY_TITLE);    }    @Nullable    @Override    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,            @Nullable Bundle savedInstanceState) {        getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);        // 圆角背景        getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));        View rootView = inflater.inflate(R.layout.layout_easy_dialogfragment, container, false);        mTvTitle = rootView.findViewById(R.id.tv_title);        mTvTitle.setText(mTitle);        mBtnCancel = rootView.findViewById(R.id.btn_cancel);        mBtnConfirm = rootView.findViewById(R.id.btn_confirm);        mBtnCancel.setOnClickListener(this);        mBtnConfirm.setOnClickListener(this);        return rootView;    }}复制代码

现在在日常的开发中 Director 角色经常会被忽略,这样会相对的减少了构造的步骤而直接使用一个 Builder 来进行对象的组装,最关键的还是 Builder 通常为链式调用,它的每个 setter 方法都返回自身,也就是代码中的 return this, 这样就可以实现链式调用了。

使用EasyDialogFragment:

DialogFragment dialogFragment = new Builder().setTitle("这是标题")        .setPositiveButton(new EasyDialogFragment.OnClickListener() {            @Override            public void onClick() {                Toast.makeText(MainActivity.this, "确定", Toast.LENGTH_SHORT).show();            }        })        .setNegativeButton(new EasyDialogFragment.OnClickListener() {            @Override            public void onClick() {                Toast.makeText(MainActivity.this, "取消", Toast.LENGTH_SHORT).show();            }        })        .build();dialogFragment.show(getSupportFragmentManager(), "");复制代码

效果:

总结

最后总结一下builder模式的优缺点
优点:

  1. 良好的封装性, 使用建造者模式可以使客户端不必知道产品内部组成的细节
  2. 建造者独立, 容易扩展
  3. 链式调用使得代码更简洁、易懂 缺点: 会产生多余的builder对象以及Director对象, 消耗内存

最后

最后发现了一个自动生成builder模式的插件 InnerBuilder, 详细可以看这篇
本文代码地址: github.com/mundane7996…

更多相关文章

  1. dex2oat的原理及慢的原因
  2. 我的Android进阶之旅------>android Toast大全(五种情形)建立属于
  3. 一段android跳转到web界面的代码
  4. android task与back stack 开发文档翻译 - 2
  5. Android(安卓)Studio中svn的使用(全部在这里)
  6. 请求网络数据后更新视图
  7. [置顶] Android(安卓)Studio快捷键
  8. Android(安卓)Studio 快捷键 (Mac OS X)
  9. Android观察者模式实例分析

随机推荐

  1. Android(安卓)数据库初窥
  2. android 蓝牙4.0广播功能应用
  3. Android Studio 实时显示布局文件Preview
  4. Android实现TCP与UDP传输
  5. Android 应用冻结流程分析
  6. Android 屏幕适配方案
  7. 设置ListView每条数据之间的间隔
  8. Android Animation动画详解(一): 补间动画
  9. Android——RecyclerView.scrollBy源码分
  10. 【Android 系统开发】Android JNI 之 JNI