定义
复杂的构建与表示分离,使得同样的构建过程可以有不同的表示。
使用场景
- 相同方法不同的执行顺序,产生不同的结果。
- 多个部件或者零件装配到一个对象中,但是产生的运行结果又不相同时候。
- 产品类型非常复杂,或者产品类中的调用顺序不同产生不同的作用,这个时候使用建造者模式非常合适。
- 初始化一个对象特别复杂,如参数多,且很多参数都有默认值。
UML类图
- builder
- 抽象类,规范产品的组建,一般由子类实现具体的过程。
- ConcreteBuilder:
- 具体的builder类。
- Director
- 构造一个使用Builder接口的对象.
- Product
- 产品类的抽象。 ConcreteBuilder创建该产品的内部表示并定义它的装配过程。
- 包含定义组成部件的类,包括将这些部件装配成最终产品的接口。
Builder模式的简单实现
场景:假设客户需要建造一个房子,于是他就会找到一个建筑公司(Director),而建筑公司找到包工头(builder)来建造房子。包工头也不会自己去建房子,他让一些工人(ConcreteBuilder)去建造房子。
角色
builder(包工头)
给出一个抽象接口,定义了各个工人所需要进行的工作。这些工作是为了完成对房子的创建。
ConcreteBuilder(工人)
具体建造者,具体的建造者(工人)去造房子。房子造完后需要将房子还给房屋主人 所以需要有返回房子的方法。
Director(建筑公司)
- 他知道房子怎么设计
- 他会指挥工人去建造:代码中 ,他也肯定会持有工人 的这个对象的引用
- 所以他肯定对工人所具备的能力和行为有很深的了解。
- 从整体角度出发,什么样的房子都能跟你建成。他所有具备的功能可以覆盖你完整的需求。哪怕业主只提出建个非常简单的房子,但是他所具备的能力必须全部覆盖:代码中,所有提出的需求在设计者这个类里面都能找得到。
具体代码
product类,房子:
package com;
//房子类 即product角色
public class Room {
//窗户
private String window;
//地板
private String floor;
public String getWindow() {
return window;
}
public void setWindow(String window) {
this.window = window;
}
public String getFloor() {
return floor;
}
public void setFloor(String floor) {
this.floor = floor;
}
@Override
public String toString() {
return "Room [window=" + window + ", floor=" + floor + "]";
}
}
抽象builder类,包工头:
/**
* 抽象builder类
*
*/
public interface Builder {
//设置窗户
public void makeWindow();
//设置地板
public void makeFloor();
//创建房子
public Room build();
}
ConcreteBuilder,具体builder类,工人,持有对房子的引用。
package com;
/**
* 具体builder类,持有对房子的引用。
* @author xijun
*
*/
public class WorkBuilder implements Builder{
Room room = new Room();
@Override
public void makeWindow() {
room.setWindow("磨砂窗户");
}
@Override
public void makeFloor() {
room.setFloor("美式地板");
}
@Override
public Room build() {
return room;
}
}
Director,建筑公司,持有对包工头(builder)的引用。
package com;
public class Director {
public Room build(Builder build) {
build.makeFloor();
build.makeWindow();
return build.build();
}
}
测试代码,client类:
package com;
public class Client {
public static void main(String[] args) {
Builder workBuild = new WorkBuilder();
Director designer = new Director();
Room room = designer.build(workBuild);
System.out.println(room.toString());
}
}
输出结果:
Room [window=法式窗户, floor=欧式地板]
builder模式变型
实际开发中,Director通常会被省略,client端通常扮演指挥者的角色,concreteBuilder直接来进行对象的组装,省去了builder,通过链式调用来达到目的。
大致代码如下:
new TestBuilder("a").set().set("b").build();
这种方法适合于:初始化一个对象特别复杂,如参数多,且很多参数都有默认值。
将上面的代码进行builder变型
我们先不探讨这几个类的具体意义,
先将上面的代码进行变型为这种链式调用方式:
WorkBuilder类:
package com;
/**
* 具体builder类,
* @author xijun
*
*/
public class WorkBuilder {
RoomParams roomParams;
public WorkBuilder() {
this.roomParams = new RoomParams();
}
//每个方法都会返回自身WorkBuilder,以便可以链式调用。
public WorkBuilder makeWindow(String window) {
roomParams.window = window;
return this;
}
//每个方法都会返回自身WorkBuilder,以便可以链式调用。
public WorkBuilder makeFloor(String floor) {
roomParams.floor = floor;
return this;
}
public Room build() {
Room room = new Room();
//通过apply方法将RoomParams类中的成员变量传递到Room中。
room.apply(roomParams);
return room;
}
class RoomParams {
public String window;
public String floor;
}
}
其中所有的调用方法(makeWindow(),makeFloor())返回了WorkBuilder本身,这样可以链式调用,直到调用build()方法。
其中RoomParams是WorkBuilder的内部类,和Room的成员变量是一样的,这样以便于将RoomParams的成员变量传到Room中去。通过
Room room = new Room();
room.apply(roomParams);
这段代码可以将这些应用的参数传递到Room类的成员变量中。
我们看一下Room类。
package com;
import com.WorkBuilder.RoomParams;
/**
* 房子类,产品角色
*/
public class Room {
private String window;
private String floor;
//通过apply方法将RoomParams类中的成员变量传递到Room中。
public void apply(RoomParams roomParams) {
this.window = roomParams.window;
this.floor = roomParams.floor;
}
@Override
public String toString() {
return "Room [window=" + window + ", floor=" + floor + "]";
}
}
这样我们的client端就可以链式调用了。
package com;
public class Client {
public static void main(String[] args) {
Room room = (new WorkBuilder())
.makeFloor("diban")
.makeWindow("window").build();
System.out.println(room.toString());
}
}
输出结果:
Room [window=window, floor=diban]
Android AlertDialog就是builder模式的变型
AlertDialog的用法会是这样的链式调用:
new AlertDialog.Builder(MainActivity.this).setTitle("标题").setIcon(R.mipmap.ic_launcher).show();
AlertDialog通过buiilder模式将 AlertDialog的构造和表示分离。
我们看看源码:
//\frameworks\base\core\java\android\app\AlertDialog
public class AlertDialog extends Dialog implements DialogInterface {
// Controller, 接受Builder成员变量P中的各个参数
private AlertController mAlert;
// 构造函数AlertDialog
AlertDialog(Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
...
//构造AlertController.
mAlert = AlertController.create(getContext(), this, getWindow());
}
...
@Override
public void setTitle(CharSequence title) {
super.setTitle(title);
//调用AlertController的setTitle。
mAlert.setTitle(title);
}
/**
* @see Builder#setCustomTitle(View)
*/
public void setCustomTitle(View customTitleView) {
mAlert.setCustomTitle(customTitleView);
}
public void setMessage(CharSequence message) {
mAlert.setMessage(message);
}
//AlertDialog中的内部类。
public static class Builder {
//AlertController.AlertParams就是存储各种成员变量参数,如title,message.
private final AlertController.AlertParams P;
public Builder(Context context, int themeResId) {
P = new AlertController.AlertParams(new ContextThemeWrapper(
context, resolveDialogTheme(context, themeResId)));
}
public Context getContext() {
return P.mContext;
}
//设置title,也是设置到了AlertController.AlertParams中。
public Builder setTitle(CharSequence title) {
P.mTitle = title;
return this;
}
public Builder setCustomTitle(View customTitleView) {
P.mCustomTitleView = customTitleView;
return this;
}
//构建AlertDialog, 传递参数
public AlertDialog create() {
//调用new AlertDialog构造对象, 并且将参数传递个体AlertDialog
final AlertDialog dialog = new AlertDialog(P.mContext, 0, false);
//通过调用Apply函数来给P设置参数。
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;
}
public AlertDialog show() {
final AlertDialog dialog = create();
dialog.show();
return dialog;
}
}
}
AlertController类中的内部类AlertParams:
public static class AlertParams {
//各种各样的参数。
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;
...
//通过apply这个函数来设置参数。
public void apply(AlertController dialog) {
if (mCustomTitleView != null) {
dialog.setCustomTitle(mCustomTitleView);
} else {
if (mTitle != null) {
//设置title。
dialog.setTitle(mTitle);
}
if (mIcon != null) {
dialog.setIcon(mIcon);
}
if (mIconId != 0) {
dialog.setIcon(mIconId);
}
if (mIconAttrId != 0) {
dialog.setIcon(dialog.getIconAttributeResId(mIconAttrId));
}
}
}
通过java控制台来体会 AlertDialog
我将AlertDialog中的builder模式代码进行删减,以便更加直观和深入的体会其中的模式。控制台中输出:
title是:123
client端:
package com;
import com.AlertDialog.Builder;
public class Client {
public static void main(String[] args) {
new AlertDialog.Builder().setTitle("123").show();
}
}
AlertDialog类:
package com;
public class AlertDialog {
private AlertController mAlert;
public AlertDialog() {
mAlert = new AlertController().create();
}
static class Builder{
private AlertController.AlertParams p;
Builder(){
p=new AlertController.AlertParams();
}
public Builder setTitle(String title) {
p.title = title;
return this;
}
public AlertDialog show() {
AlertDialog alertDialog = new AlertDialog();
p.apply(alertDialog.mAlert);
return alertDialog;
}
}
}
AlertController:
package com;
public class AlertController {
private String mTitle;
public AlertController create() {
return new AlertController();
}
public void setTitle(String title) {
mTitle = title;
System.out.println("title是:"+ title);
}
public static class AlertParams {
String title;
public void apply(AlertController mAlert) {
if(title != null) {
mAlert.setTitle(title);
}
}
}
}
StringBuilder就是builder模式的变型
这种方式调用具体也有很多的使用场景,例如java中的StringBuilder:
在这个Builder模式的实现中,Client同时充当了Director的角色;StringBuilder同时充当了Builder接口和ConcreteBuilder。这是一个最简化的Builder模式的实现。
1:
//Client同时充当了Director的角色
2:
StringBuilder builder = new
StringBuilder();
3:
builder.Append("happyhippy"
);
4:
builder.Append(".cnblogs"
);
5:
builder.Append(".com"
);
6:
//返回string对象:happyhippy.cnblogs.com
7:
builder.ToString();
参考:
《Android源码设计模式解析与实战》
Builder模式的误区:将复杂对象的构建进行封装,就是Builder模式了吗?