Builder设计模式又叫做建造者模式
定义:
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
使用场景:
1、初始化一个对象时,有许多参数,并且好多都有默认值
2、一个类非常复杂,当调用类中的方法顺序不同时,结果也不同
如果有这些情况的时候,就可以考虑使用Builder模式来构建你的类了
Builder模式的UML图:
Director:代表统一组装的类
Builder:代表了抽象的Builder类,这里面规范产品的组成,一般由子类做具体的实现
ConcreteBuilder:代表了具体的Builder类
Product:具体的产品
我们先看看Android系统的源码中是如何使用Builder模式的。咱们以AlertDialog为例来讲解。
一般情况下我们都是这么使用AlertDialog的:
AlertDialog alertDialog = new AlertDialog.Builder(this)
.setTitle("提示")
.setMessage("我是一个弹框")
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(MainActivity.this,"点击了确定",Toast.LENGTH_LONG).show();
}
})
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(MainActivity.this,"点击了取消",Toast.LENGTH_LONG).show();
}
})
.setCancelable(false)
.create();
alertDialog.show();
这段代码执行之后就会在在界面上弹出一个对话框
我们闲简单的画一个AlertDialog的UML图:
这里我只列出来了一些主要的参数和方法,
AlertDialog对应的就是我们的Product
Builder就是具体的ConcreteBuilder,这里面没有用到抽象的Builder
AlertParams代表Director类
我们先看一下AlertDialog.Builder的源码:
public static class Builder {
private final AlertController.AlertParams P;
public Builder(Context context) {
this(context, resolveDialogTheme(context, ResourceId.ID_NULL));
}
public Builder(Context context, int themeResId) {
P = new AlertController.AlertParams(new ContextThemeWrapper(
context, resolveDialogTheme(context, themeResId)));
}
...
public Builder setTitle(CharSequence title) {
P.mTitle = title;
return this;
}
...
public Builder setMessage(CharSequence message) {
P.mMessage = message;
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;
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mAlert.installContent();
}
public AlertDialog show() {
final AlertDialog dialog = create();
dialog.show();
return dialog;
}
}
我这里做了一些删减,Builder中持有AlertParams的引用,用来保存dialog的一些参数,当我们调用create()的时候,会创建一个dialog,内部会去调用P的apply()方法。这个方法需要穿一个参数dialog.mAlert,这个参数来自AlertDialog,代码如下:
public class AlertDialog extends Dialog implements DialogInterface {
private AlertController mAlert;
...
AlertDialog(Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
super(context, createContextThemeWrapper ? resolveDialogTheme(context, themeResId) : 0,
createContextThemeWrapper);
mWindow.alwaysReadCloseOnTouchAttr();
mAlert = AlertController.create(getContext(), this, getWindow());
}
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mAlert.installContent();
}
}
我们可以看到mAlert在构造方法中被初始化。再次回到P的apply方法中,在AlertParams的apply()方法中会一个一个的调用AlertController的setTitle()、setMessage()方法。
最后当我们调用Builder的show()方法的时候,内部会调用AlertDialog的生命周期方法onCreate()方法,在该方法的内部,调用了AlertController的installContent()方法,最终在屏幕上显示出Dialog。
现在让我们模仿AlertDialog的源码,写一个自己的builder模式。这个例子模拟的是组装一台电脑。
首先我们创建一个实体类Conputer,也就是Product。
public class Computer {
//电脑名称
private String mName;
//cpu核心数
private int mBoard;
//显示器
private String mDisplay;
//操作系统
private String mOs;
Computer(){}
void setName(String name){
mName = name;
}
void setBoard(int board){
mBoard = board;
}
void setDisplay(String display){
mDisplay = display;
}
void setOs(String os){
mOs = os;
}
@NonNull
@Override
public String toString() {
return "电脑信息:\n型号:"+mName+"\nCPU数:"+mBoard+"\n显示屏:"+mDisplay+"\n操作系统:"+mOs;
}
}
在这个类里面有一些参数和方法用来保存电脑的信息,我们重写了toString方法用来打印电脑的信息。
接着我们需要创建一个Params类,这个类就是ConcreteBuilder类。
class ComputerParams {
//电脑名称
private String mName;
//cpu核心数
private int mBoard;
//显示器
private String mDisplay;
//操作系统
private String mOs;
void setName(String name){
mName = name;
}
void setBoard(int board){
mBoard = board;
}
void setDisplay(String display){
mDisplay = display;
}
void setOs(String os){
mOs = os;
}
void apply(Computer computer){
if (mName!=null){
computer.setName(mName);
}
if(mBoard>0){
computer.setBoard(mBoard);
}
if (mDisplay!=null){
computer.setDisplay(mDisplay);
}
if (mOs!=null){
computer.setOs(mOs);
}
}
}
最后我们创建一个Builder类。
public class ComputerBuilder {
private ComputerParams mParams;
public ComputerBuilder(){
if (mParams==null){
mParams = new ComputerParams();
}
}
public ComputerBuilder setName(String name){
mParams.setName(name);
return this;
}
public ComputerBuilder setBoard(int board){
mParams.setBoard(board);
return this;
}
public ComputerBuilder setDisplay(String display){
mParams.setDisplay(display);
return this;
}
public ComputerBuilder setOs(String os){
mParams.setOs(os);
return this;
}
public Computer builder(){
Computer computer = new Computer();
mParams.apply(computer);
return computer;
}
}
这里我简单的说明一下,AlertDialog的源码中把Product和Builder写在了一个文件中。Builder是Product的一个内部类。这里我们为了看的明显一点,将它们拆开成了两个文件。
我将Computer和ComputerParams的方法对外进行了隐藏处理。这样一来,别人使用的时候,就只能通过ComputerBuilder这个类进行Computer的创建。
在ComputerBuilder的类中又一个builder方法,在这个方法的内部会实例化一个Computer的对象,然后嗲用ComputerParams的apply方法对Conputer进行参数赋值。最后将构建好的computer返回。这样我们就模拟AlertDialog的Builder模式创建出来了一台电脑。下面是它的使用方式:
public static void main(String[] args) {
Test test = new Test();
test.createComputer();
}
private void createComputer(){
Computer computer = new ComputerBuilder()
.setName("MacBook Pro 2019")
.setBoard(8)
.setDisplay("内建视网膜LCD")
.setOs("Mac os 10.15")
.builder();
System.out.println(computer.toString());
}
在控制台上会打印出这台电脑的信息: