一、定义
讲一个复杂对象的构建与他的表示分离,使用同样的构建过程实现不同的表示;
- 分类:创建型模式
二、使用场景
相同的方法,不同的执行顺序,产生不一样的事件结果;
将多个零件(部分),通过不同的执行顺序,而产生不同表示结果;
-
产品类非常复杂,如:参数太多,很多参数都具有默认值,或通过不同的顺序,获取不同的表示结果;
核心:不同的方法顺序,获取不同的表示结果;
三、UML类图
标准Builder模式
简单Builder模式
Builder的变种
备注:Bean 存储数据的实体,在实体内部实现Builder;
角色介绍:
-
Product 产品(抽象)类
可以是具体的产品,也可以是产品的抽象类,对产品的相关进行设计或实现; -
Buider抽象类
用于编写Builder的抽象,规范产品的组建; -
ConcreteBuilder实现类
实现对产品的具体的操作流程; -
Director(主管类)
实现Builder的统一组装过程;
备注:一般实现过程中,经常会将主管类省略,直接使用Builder对产品进行操作,它会返回this对象,以此来实现Builder的链式调用,这种模式,我管他叫简单Builder模式;该方法,结构更加简单,对比标准结构也没有明显的缺点,推荐使用这种模式;
四、代码实现
代码以顾客在苹果专卖店挑选手机为例。顾客挑选手机时,会根据系统、cpu、屏幕等等参数进行选择手机;
1. 标准Builder模式
/**
* Created by zhangxuehui on 2017/6/18.
* 苹果专卖店
*/
public class BuilderAppleShop {
public static void main(String[] args) {
Builder builder = new AppleBuilder();
Director director = new Director(builder);
director.buyMoble("A9", "4.7");
System.out.println("用户购买的第一部手机是:" + buildr.create().toString());
director.buyMoble("ios 11", "A10", "5.5");
System.out.println("用户购买的第二部手机是:" + buildr.create().toString());
}
/**
* 手机配置抽象类,标准化手机的核心参数
*/
static abstract class Mobile {
protected String logo;//品牌
protected String cpu;//cpu处理器
protected String os;//系统
protected String screenSize;//屏幕尺寸
abstract void logo();//苹果专卖店只有苹果的产品,所以logo相当于手机标志,必须由具体的手机类实现;
public String getCpu() {
return cpu;
}
public void setCpu(String cpu) {
this.cpu = cpu;
}
public abstract void os();//手机一定会有系统,需要有一个默认的系统,可以有用户动态设置
public void setOs(String os) { //顾客可以选择不同版本的系统,
this.os = os;
}
public String getScreenSize() {
return screenSize;
}
public void setScreenSize(String screenSize) {
this.screenSize = screenSize;
}
@Override
public String toString() {
return "Mobile{" +
"logo='" + logo + '\'' +
", cpu='" + cpu + '\'' +
", os='" + os + '\'' +
", screenSize='" + screenSize + '\'' +
'}';
}
}
/**
* 苹果手机类--产品的具体实现类
*/
static class AppleMobile extends Mobile {
@Override
void logo() {
logo = "apple";
}
@Override
public void os() {
os = "ios 10";
}
}
/**
* 建造者的抽象类,规范操作以及流程
*/
static abstract class Builder {
abstract void builderLogo();
abstract void builderOs();
abstract void builderOs(String os);
abstract void builderScreenSize(String size);
abstract void builderCpu(String cpu);
abstract Mobile create();
}
/**
* 建造者的具体实现类
*/
public static class AppleBuilder extends Builder {
Mobile mobile = new AppleMobile();
@Override
void builderLogo() {
mobile.logo();
}
@Override
void builderOs() {
mobile.os();
}
@Override
void builderOs(String os) {
mobile.setOs(os);
}
@Override
void builderScreenSize(String size) {
mobile.setScreenSize(size);
}
@Override
void builderCpu(String cpu) {
mobile.setCpu(cpu);
}
@Override
Mobile create() {
return mobile;
}
}
/**
* 主管类,对Builder进行统一操作
*/
public static class Director {
Builder mBuildr = null;
public Director(Builder mBuildr) {
this.mBuildr = mBuildr;
mBuildr.builderLogo();
}
//购买自选系统版本的手机
public void buyMoble(String os, String cpu, String size) {
mBuildr.builderOs(os);
mBuildr.builderCpu(cpu);
mBuildr.builderScreenSize(size);
}
//购买官方默认系统的手机
public void buyMoble(String cpu, String size) {
mBuildr.builderCpu(cpu);
mBuildr.builderScreenSize(size);
mBuildr.builderOs();
}
}
}
备注:
- 引用产品的具体实现时,实例的类型应为其抽象类;
- Builder类中,一定要实现create()方法;
- 要对产品层、Builder层、Directer层,进行合理划分,Builder仅处理产品,Directer仅处理Builder;不可以出现,Directer处理产品类的情况;
输出结果
2. 简单Builder模式 (进化版)
/**
* Created by zhangxuehui on 2017/6/18.
* 苹果专卖店
*/
public class SimpleBuilderAppleShop {
public static void main(String[] args) {
Builder builder = new AppleBuilder();
Mobile mobile1 = builder.builderScreenSize("5.5").builderCpu("A10").create();
System.out.println("用户购买的第一部手机是:" + mobile1.toString());
Mobile mobile2 = builder.builderScreenSize("4.7").builderCpu("A9").builderOs("ios 7").create();
System.out.println("用户购买的第二部手机是:" + mobile2.toString());
}
/**
* 手机配置抽象类,标准化手机的核心参数
*/
static abstract class Mobile {
protected String logo;//品牌
protected String cpu;//cpu处理器
protected String os;//系统
protected String screenSize;//屏幕尺寸
abstract void logo();//苹果专卖店只有苹果的产品,所以logo相当于手机标志,必须由具体的手机类实现;
public String getCpu() {
return cpu;
}
public void setCpu(String cpu) {
this.cpu = cpu;
}
public abstract void os();//手机一定会有系统,需要有一个默认的系统,可以有用户动态设置
public void setOs(String os) { //顾客可以选择不同版本的系统,
this.os = os;
}
public String getScreenSize() {
return screenSize;
}
public void setScreenSize(String screenSize) {
this.screenSize = screenSize;
}
@Override
public String toString() {
return "Mobile{" +
"logo='" + logo + '\'' +
", cpu='" + cpu + '\'' +
", os='" + os + '\'' +
", screenSize='" + screenSize + '\'' +
'}';
}
}
/**
* 苹果手机类--产品的具体实现类
*/
static class AppleMobile extends Mobile {
@Override
void logo() {
logo = "apple";
}
@Override
public void os() {
os = "ios 10";
}
}
/**
* 建造者的抽象类,规范操作以及流程
*/
static abstract class Builder {
abstract Builder builderLogo();
abstract Builder builderOs();
abstract Builder builderOs(String os);
abstract Builder builderScreenSize(String size);
abstract Builder builderCpu(String cpu);
abstract Mobile create();
}
/**
* 建造者的具体实现类
*/
public static class AppleBuilder extends Builder {
Mobile mobile = new AppleMobile();
//在构造方法中初始化默认值
public AppleBuilder() {
builderLogo();
builderOs();
}
@Override
Builder builderLogo() {
mobile.logo();
return this;//除去create()全部返回this,方便链式调用。
}
@Override
Builder builderOs() {
mobile.os();
return this;
}
@Override
Builder builderOs(String os) {
mobile.setOs(os);
return this;
}
@Override
Builder builderScreenSize(String size) {
mobile.setScreenSize(size);
return this;
}
@Override
Builder builderCpu(String cpu) {
mobile.setCpu(cpu);
return this;
}
@Override
Mobile create() {
return mobile;//将设置好的对象返回;
}
}
}
备注:
- 与标准模式的主要区别,在于完全去除了Direct类,用户直接操作Builder;
- Builder类中,除create()其他全部返回this,便于链式调用;
- 用户直接获取最终配置好的产品类;
输出结果
3. Builder的变种
public class User {
private final String Name;//姓名 必选
private final int idCode;//身份证 必选
private final int age;//年龄 可选
private final String like;//爱好 可选
private final String language;//语言 可选
private final String address;//地址 可选
//传人Builder对实体设置参数
public User(Builder builder) {
this.Name = builder.name;
this.idCode = builder.idCode;
this.age = builder.age;
this.like = builder.like;
this.language = builder.language;
this.address = builder.address;
}
@Override
public String toString() {
return "User{" +
"Name='" + Name + '\'' +
", idCode=" + idCode +
", age=" + age +
", like='" + like + '\'' +
", language='" + language + '\'' +
", address='" + address + '\'' +
'}';
}
/**
* 实现builder内部类,用于配置实体的参数
*/
public static class Builder {
//拷贝一份与user实体相同的参数,并且不设置为final
private String name;//姓名 必选
private int idCode;//身份证 必选
private int age;//年龄 可选
private String like;//爱好 可选
private String language;//语言 可选
private String address;//地址 可选
//对必选的参数在构造方法中进行设置
public Builder(String name, int idCode) {
this.name = name;
this.idCode = idCode;
}
//参数设置时,应该返回当前类对象,用于链式调用
public Builder setAge(int age) {
this.age = age;
return this;
}
public Builder setLike(String like) {
this.like = like;
return this;
}
public Builder setLanguage(String language) {
this.language = language;
return this;
}
public Builder setAddress(String address) {
this.address = address;
return this;
}
//配置完参数后,将builder传递给User,完成创建。
protected User create() {
return new User(this);
}
}
}
使用
public static void main(String[] args) {
User u = new User.Builder("张山",123)
.setAddress("北京市")
.setAge(18)
.setLanguage("中文")
.setLike("游泳")
.create();
System.out.println(u.toString());
}
备注
1.该模式适用于,需要的初始化操作,或构造方法的时候需要传人大量的参数;
五、android源码中的实现(Dialog)
public class AlertDialog extends AppCompatDialog implements DialogInterface {
final AlertController mAlert;//Dialog的控制类,所有逻辑都在其内部实现
/********无关代码忽略********/
/**
* Construct an AlertDialog that uses an explicit theme. The actual style
* that an AlertDialog uses is a private implementation, however you can
* here supply either the name of an attribute in the theme from which
* to get the dialog's style (such as {@link R.attr#alertDialogTheme}.
* 核心构造方法,用于绑定AlertController
*/
protected AlertDialog(@NonNull Context context, @StyleRes int themeResId) {
super(context, resolveDialogTheme(context, themeResId));
mAlert = new AlertController(getContext(), this, getWindow());
}
//对外开放的按钮,
public Button getButton(int whichButton) {
return mAlert.getButton(whichButton);
}
/*这一部分,是对外开放的view操作,如设置按钮、图标、文字等*/
@Override
protected void onCreate(Bundle savedInstanceState) {//与生命周期绑定
super.onCreate(savedInstanceState);
mAlert.installContent();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {//对按键进行处理
if (mAlert.onKeyDown(keyCode, event)) {
return true;
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {//对按键进行处理
if (mAlert.onKeyUp(keyCode, event)) {
return true;
}
return super.onKeyUp(keyCode, event);
}
/**
* 重点来啦,AlertDialog的builder类
**/
public static class Builder {
//核心参数,在Builder中配置的参数都会赋值给它,再由它转交给AlertController,最后由AlertDialog,直接获取Controller;
private final AlertController.AlertParams P;
private final int mTheme;
/**
* 构造,必填参数context,略
*/
public Builder(@NonNull Context context) {
this(context, resolveDialogTheme(context, 0));
}
/**
* 构造,必填参数context和Theme,略
*/
public Builder(@NonNull Context context, @StyleRes int themeResId) {
P = new AlertController.AlertParams(new ContextThemeWrapper(
context, resolveDialogTheme(context, themeResId)));
mTheme = themeResId;
}
@NonNull
public Context getContext() {
return P.mContext;
}
/**
* Set the title using the given resource id.
*
* @return This Builder object to allow for chaining of calls to set methods
*/
public Builder setTitle(@StringRes int titleId) {
P.mTitle = P.mContext.getText(titleId);
return this;
}
/**
* 这一部分为,配置标题、图片、文字、listview、监听事件等,所有的配置参数都会通过P配置,如:P.mTitle = P.mContext.getText(titleId),其他方法基本与上边相同,不解释;
*/
/**
* 重点看看它的create实现。
* 1.初始化AlertDialog;
* 2.将AlertController.AlertParams中的参数配置给AlertDialog。
* 3.配置监听事件,并返回AlertDialog对象
*/
public AlertDialog create() {
// We can't use Dialog's 3-arg constructor with the createThemeContextWrapper param,
// so we always have to re-set the theme
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;
}
}
}
总结
源码中的Builder使用的很有趣,他没有像我们常见的讲参数直接配置给对象,而是通过一个Controller类实现逻辑,Controller的内部类AlertParams配置它的参数,而不论Builder类还是AlertDialog类都只是一个入口。当然一切都从Builder开始。