内容简介:介绍了经典实现以及我们日常的实现方法。最后选取了一个 android 源码中的 Build 设计模式—— AlertDialog 进行分析。
前言
在我们的日常开发中,会经常用到 builder 设计模式,最常见的应用场景就是构造对象参数较多的时候,下面本文将 builder 模式梳理总结一下。
定义
如果说非要给 builder 模式一个定义,那通过查看了《Android源码设计模式解析与实战》,以下是其给出的定义:
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
这样的总结比较经典,但是感觉离彻底明白其中的含义还差点距离。前半句可以理解为将一个对象的创建过程分多步,后半句可以这样理解,使用同样的构建过程,传递不同的参数会产生不同的结果。
经典实现
上图为 builder 经典写法的 uml ,其实在实际开发过程中, Director 部分经常就被去掉了。 如下所示,定义了 AbstractBuilder :
public abstract class AbstractBuilder {
public abstract void buildPart1(int numOfWheel);
public abstract void buildPart2(int numOfSeat);
public abstract void buildPart3(int capacity);
public abstract Vehicle build();
}
demo 的目标是将车辆 Vehicle 的构建分离, demo 中的 Product 为 Vehicle 类型,可以看下 Vehicle 的定义:
public abstract class Vehicle {
// 车轮的数量
protected int numOfWheel;
// 座椅的数量
protected int numOfSeat;
// 载重,car按照人数,truck按照吨数
protected int capacity;
public void setNumOfWheel(int numOfWheel) {
this.numOfWheel = numOfWheel;
}
public void setNumOfSeat(int numOfSeat) {
this.numOfSeat = numOfSeat;
}
public abstract void setCapacity(int capacity);
}
demo 中 Vehicle 的子类有 Car (小轿车)和 Truck (卡车), Car 类的定义如下:
public class Car extends Vehicle {
@Override
public void setCapacity(int capacity) {
// TODO Auto-generated method stub
super.capacity = capacity;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return "car parameter:" + numOfWheel + "-" + numOfSeat + "-" + capacity;
}
}
Truck 的定义如下:
public class Truck extends Vehicle {
@Override
public void setCapacity(int capacity) {
// TODO Auto-generated method stub
super.capacity = capacity;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return "car parameter:" + numOfWheel + "-" + numOfSeat + "-" + capacity + " ton";
}
}
接下来就看下 Car 的 builder CarBuilder :
public class CarBuilder extends AbstractBuilder {
private Car car = new Car();
@Override
public void buildPart1(int numOfWheel) {
// TODO Auto-generated method stub
car.setNumOfWheel(numOfWheel);
}
@Override
public void buildPart2(int numOfSeat) {
// TODO Auto-generated method stub
car.setNumOfSeat(numOfSeat);
}
public void buildPart3(int capacity) {
// TODO Auto-generated method stub
car.setCapacity(capacity);
}
public Vehicle build() {
return car;
}
}
TruckBuilder 定义如下:
public class TruckBuilder extends AbstractBuilder {
private Truck truck = new Truck();
@Override
public void buildPart1(int numOfWheel) {
// TODO Auto-generated method stub
truck.setNumOfWheel(numOfWheel);
}
@Override
public void buildPart2(int numOfSeat) {
// TODO Auto-generated method stub
truck.setNumOfSeat(numOfSeat);
}
@Override
public void buildPart3(int capacity) {
// TODO Auto-generated method stub
truck.setCapacity(capacity);
}
@Override
public Vehicle build() {
// TODO Auto-generated method stub
return truck;
}
}
以上定义了 CarBuiler , TruckBuiler 。虽然在实际开发中经常会省略掉 Director 部分,为了演示, demo 也定义了 Director
public class Director {
private AbstractBuilder builder;
public Director(AbstractBuilder builder) {
this.builder = builder;
}
public void construct(int numOfWheel, int numOfSeat, int capacity) {
if (builder != null) {
builder.buildPart1(numOfWheel);
builder.buildPart2(numOfSeat);
builder.buildPart3(capacity);
}
}
}
OK,所有需要定义的部分已经完成,接下来就去调用一下:
public class Client {
public static void main(String [] args) {
AbstractBuilder builder = new CarBuilder();
Director director = new Director(builder);
director.construct(4, 5, 5);
Car car = (Car) builder.build();
System.out.println(car);
AbstractBuilder builder2 = new TruckBuilder();
Director director2 = new Director(builder2);
director2.construct(8, 2, 5);
Truck truck = (Truck) builder2.build();
System.out.println(truck);
}
}
程序输入如下:
car parameter:4-5-5
truck parameter:8-2-5 ton
demo演示部分将Vehicle的构造过程分3步,执行完3步构建后返回实例对象。
日常写法
上面的 demo 是经典的写法,但在实际开发中,很少写的那么标准或者那么复杂,大多数情况下 builder 模式主要是为了防止在构建对象时传递太多的参数。查看下以下 demo :
public class Student {
private String name;
private String nickName;
private String sex;
private int age;
private int weight;
private int height;
public Student(String name, String nickName, String sex, int age, int weight, int height) {
// TODO Auto-generated constructor stub
this.name = name;
this.nickName = nickName;
this.sex = sex;
this.age = age;
this.weight = weight;
this.height = height;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return "student info:name=" + name + "\n" +
"nickname=" + nickName + "\n" +
"sex=" + sex + "\n" +
"age=" + age + "\n" +
"weight=" + weight + "\n" +
"height=" + height;
}
public static class Builder {
private String name;
private String nickName;
private String sex;
private int age;
private int weight;
private int height;
public Builder name(String name) {
this.name = name;
return this;
}
public Builder nickName(String nickName) {
this.nickName = nickName;
return this;
}
public Builder sex(String sex) {
this.sex = sex;
return this;
}
public Builder age(int age) {
this.age = age;
return this;
}
public Builder weight(int weight) {
this.weight = weight;
return this;
}
public Builder height(int height) {
this.height = height;
return this;
}
public Student build() {
return new Student(name, nickName, sex, age, weight, height);
}
}
}
以下是测试程序:
public class Client {
public static void main(String [] args) {
Student student = new Student.Builder().name("rock")
.nickName("store")
.sex("boy")
.age(12)
.weight(60)
.height(176).build();
System.out.println(student);
}
}
程序运行结果如下:
student info:name=rock
nickname=store
sex=boy
age=12
weight=60
height=176
Android中的使用
在开发过程中,经常使用的 builder 模式其实就是上文所说的日常写法,Android 中最常见的 builder 模式就是 AlertDialog 的创建过程了,以下是 AlertDialog 创建过程的常见写法。
AlertDialog.Builder builder = new AlertDialog.Builder(context)
.setTitle(title)
.setView(view)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
})
.setNegativeButton(android.R.string.cancel, null);
builder.create().show();
感觉很熟悉,这就是我们最常用的 AlertDialog 的构建过程。扒一扒源码,由于 AlertDialog.Builder 的源码较多,就不全部贴出来,感兴趣的同学可以自行看一下。
public static class Builder {
private final AlertController.AlertParams P;
public Builder(Context context) {
this(context, resolveDialogTheme(context, ResourceId.ID_NULL));
}
......
public Builder setTitle(@StringRes int titleId) {
P.mTitle = P.mContext.getText(titleId);
return this;
}
public Builder setTitle(CharSequence title) {
P.mTitle = title;
return this;
}
public Builder setCustomTitle(View customTitleView) {
P.mCustomTitleView = customTitleView;
return this;
}
......
public Builder setMessage(CharSequence message) {
P.mMessage = message;
return this;
}
public Builder setPositiveButton(@StringRes int textId, final OnClickListener listener) {
P.mPositiveButtonText = P.mContext.getText(textId);
P.mPositiveButtonListener = listener;
return this;
}
......
public AlertDialog create() {
// Context has already been wrapped with the appropriate theme.
final AlertDialog dialog = new AlertDialog(P.mContext, 0, false);
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;
}
}
AlertDialog.Buidler 类中定义各种 set 方法,执行完 set 方法之后再执行 create 方法便创建了一个 AlertDialog 。
这应该是一个标准的 builder 模式了。可以发现 AlertDialog.Builder 执行 set 方法,其实就是将set参数复制给了对象 P 。对象 P 是什么结构呢?
public static class AlertParams {
public final Context mContext;
public final LayoutInflater mInflater;
public int mIconId = 0;
public Drawable mIcon;
public int mIconAttrId = 0;
public CharSequence mTitle;
public View mCustomTitleView;
public CharSequence mMessage;
public CharSequence mPositiveButtonText;
public DialogInterface.OnClickListener mPositiveButtonListener;
public CharSequence mNegativeButtonText;
public DialogInterface.OnClickListener mNegativeButtonListener;
public CharSequence mNeutralButtonText;
public DialogInterface.OnClickListener mNeutralButtonListener;
public boolean mCancelable;
public DialogInterface.OnCancelListener mOnCancelListener;
public DialogInterface.OnDismissListener mOnDismissListener;
public DialogInterface.OnKeyListener mOnKeyListener;
public CharSequence[] mItems;
public ListAdapter mAdapter;
public DialogInterface.OnClickListener mOnClickListener;
public int mViewLayoutResId;
public View mView;
public int mViewSpacingLeft;
public int mViewSpacingTop;
public int mViewSpacingRight;
public int mViewSpacingBottom;
public boolean mViewSpacingSpecified = false;
public boolean[] mCheckedItems;
public boolean mIsMultiChoice;
public boolean mIsSingleChoice;
public int mCheckedItem = -1;
public DialogInterface.OnMultiChoiceClickListener mOnCheckboxClickListener;
public Cursor mCursor;
public String mLabelColumn;
public String mIsCheckedColumn;
public boolean mForceInverseBackground;
public AdapterView.OnItemSelectedListener mOnItemSelectedListener;
public OnPrepareListViewListener mOnPrepareListViewListener;
public boolean mRecycleOnMeasure = true;
......
}
可以发现,AlertController.AlertParams 类型的对象 P 其实就是存放了构建 AlertDialog 需要的各种参数。对象 P 中还有其他函数操作,感兴趣的同学可以去看一下。
将参数保存到P对象,然后执行 create 函数,创建新的 AlertDialog 对象,然后 P 中存放的属性设置给新建的 AlertDilaog 对象,这样,就完成了 AlertDialog 的构建。
总结
Builder 模式的目标是将复杂对象的创建过程进行分解,使对象的构建与表示分离,使得同样的构建过程可以创建不同的表示。在实际开发过程中,通常是在复杂对象内部申明静态内部类 Builder,在 Builder 中保存复杂对象的属性,然后使用 create 或者 build 函数将保存的属性设置给对象。
其实日常开发过程中使用builder模式还没有让我们领略到builer模式的强大,建议参考下这篇文章体会一下:
https://www.cnblogs.com/happyhippy/archive/2010/09/01/1814287.html
--END--
识别二维码,关注我们