概述
因为一个复杂的对象有很多大量组成部分,
如汽车,有车轮、方向盘、发动机,还有各种小零件等,
如何将这些部件装配成一辆汽车,这个装配过程很漫长,也很复杂,
对于这种情况,
为了在构建过程
中对外部隐藏实现细节
,
就可以使用Builder模式
将产品的构建过程
和部件组装过程
分离,
使得产品的构建过程
和部件组装过程
都可以自由扩展,
两者之间的耦合
也降到最低
。
【一个产品的构建过程
包括多个部件组装过程
】
定义
将一个复杂对象的构建与它的表示分离, 使得同样的构建过程可以创建不同的表示。
使用场景
- (1)相同的方法,不同的执行顺序,产生不同的事件结果时。
(2)多个部件或零件,
都可以装配到一个对象中,但是产生的运行结果又不相同时。
(3)产品类
非常复杂
, 或者 产品类中部件组装过程
的调用顺序不同
产生了不同的作用
, 这个时候使用建造者模式非常合适。 (4)当初始化一个对象特别复杂,如参数多
,且很多参数都具有默认值
时!!!!
UML类图
- 角色介绍:
● Director——统一组装过程。
定义一系列的 组装统筹方法,
一个 组装统筹方法里边 包含若干个部件组装方法;
方便某方面的组装;
● Builder——抽象Builder类,
规范产品的组建,
一般是由子类实现具体的组建过程;
【以上两个成分在实际操作中经常会
省略
!!!!】 ● Product产品类——产品的抽象类; ● ConcreteBuilder——具体的Builder类;包含具体的组装过程;
实现案例【注:两种实现形式】
建造者模式个人觉得,其实可以有两种实现过程; 第一种是,
- 把部件组装方法【属性配置方法】定义在
产品类
中;!!!!! 抽象产品类
【三个元素:属性、两种方法的声明、实现】 定义好一类产品需要的属性[1] 以及需要的组装方法[2、3] 【包括抽象
的具体
的两种, 具体的是这类产品的共性
组装方法,所有分化产品都一样,无需修改;[2] 抽象的是个性
比较高 留给具体子类具体实现 [3]】;具体产品类
继承抽象产品类
,根据个性
实现抽象组装方法; 【实际开发中, 可以在抽象产品类
中或者具体产品类
中, 对属性配置好默认值
, 防止空指针报错
】- Builder类中,只是负责拿到一个产品类实例,
然后编写很多
组装方法
,组装方法
中只是使用产品类实例
调用了产品类自己的组装方法
而已;抽象Builder类
只声明需要的组装方法
;!!!!!!!!具体Builder类
面向具体产品类
, 继承抽象Builder类
的同时, 拿到一个对应的具体产品类
实例作为自己的全局成员,[1] 实现所有组装方法
,[2] 在组装方法
中使用具体产品类实例
调用了产品类
自己的组装方法
即可; 最后create()
或者build()
返回一个产品类实例,构造完毕;[3] - Director一般可以省略掉,
即整个模式有四种类文件——
抽象/具体产品类
和抽象/具体Builder类
个人心得: 这种形式存在
抽象产品类
和抽象Builder类
, 可以解耦抽象一些逻辑, 适用于具体产品类
有两个或两个以上的较大规模的开发;
如果具体产品类
在整个开发过程中确定只有一个,
那就用不着那么多花里胡俏的抽象类了,
完全可以直接去掉抽象产品类
和抽象Builder类
,
只剩下具体产品类
和具体Builder类
,
即简化版的第二种实现方式;
下面先看第一种实现案例:
//产品抽象类
public abstract class Computer {
protected String mBoard;
protected String mDisplay;
protected String mOS;
protected Computer() { }
// 设置 CPU 核心数
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 "]";
}
}
----------------------------------------------------------------------------------
//具体的 Computer 类,Macbook
public class Macbook extends Computer {
protected Macbook() {
}
@Override
public void setOS() {
mOS = "Mac OS X 10.10";
}
}
----------------------------------------------------------------------------------
//抽象 Builder 类
public abstract class Builder {
// 设置主机
public abstract void buildBoard(String board);
// 设置显示器
public abstract void buildDisplay(String displau);
// 设置操作系统
public abstract void buildOS();
// 创建 ComputerÏ
public abstract Computer create();
}
----------------------------------------------------------------------------------
//具体的 Builder 类,
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 类,负责构造 Computer
public class Director {
Builder mBuilder = null;
/**
* @param builder
*/
public Director(Builder builder) {
mBuilder = builder;
}
/**
* 构建对象
*/
public void construct(String board, String display) {
mBuilder.buildBoard(board);
mBuilder.buildDisplay(display);
mBuilder.buildOS();
}
}
----------------------------------------------------------------------------------
//测试代码
public class Test {
public static void main(String[] args) { // 构建器
Builder builder = new MacbookBuilder();
// Director
Director pcDirector = new Director(builder);
// 封装构建过程, 4 核, 内存 2GB, Mac 系统
pcDirector.construct("英特尔主板","Retina 显示器");
// 构建电脑, 输出相关信息
System.out.println("Computer Info : " builder.create().toString());
}
}
- 简化版的第二种实现方式;
如果具体产品类在整个开发过程中确定只有一个,
那就用不着那么多花里胡俏的抽象类了,
完全可以直接去掉抽象产品类和抽象Builder类,
只剩下
具体产品类
和具体Builder类
; - 另外方才第一种方法写了很多次配置方法【产品类、抽象类都写了】,
这里只需要在
具体Builder类
中写即可!!!! 具体Builder类
定义产品需要的部件属性,并在声明时初始化为默认值
!![1] 构造方法设置为默认权限
,只提供给具体产品类
的builder()
调用[2] 编写组装方法
,用于配置本类属性,注意return this;
用于连缀调用;[3] 提供build()
方法,把所有的部件属性
赋给产品类构造器
并调用它,返回一个产品实例;[4]具体产品类
定义产品需要的部件属性;(构造方法赋值所有属性)[1] 提供public static builder()
,返回一个具体Builder类
实例;[2] 实现其他产品业务
方法 [3 此部分无关构造了]
代码语言:javascript复制案例来自项目GitHub
/**
* <pre>
* desc : 使用建造者模式,构建 RestClient!!!
* 此类是建造者模式中,concreteBuilder部分
* </pre>
*/
public class RestClientBuilder {
//URL
private String mUrl = null;
//参数
private static final WeakHashMap<String, Object> PARAMS = RestCreator.getParams();
//回调
private IRequest mIRequest = null;
private ISuccess mISuccess = null;
private IFailure mIFailure = null;
private IError mIError = null;
//请求体
private RequestBody mBody = null;
//Loader
private Context mContext = null;
private LoaderStyle mLoaderStyle = null;
//文件上传
private File mFile = null;
//文件下载用 变量
private String mDownloadDir = null;//指定 下载文件 在本地sd卡的 目录名
private String mExtension = null;
private String mName = null;
//除了 RestClient(同包内的类),不允许外部 直接new 创建!!!
//这里没 声明权限,即使用 Java 的 默认权限,
// 即 只有 本类 同包 可以访问!!!只有 同包内的子类可以继承!!!!!
RestClientBuilder() {
}
/*
下面是本建造者模式的 一系列组装方法
*/
//觉得某个方法已经很完善了,不需要别人修改它,可以加上final,
// 这样也可以让JVM进行优化(如,禁止重排序)
public final RestClientBuilder url(String url) {
this.mUrl = url;
return this;
}
public final RestClientBuilder params(WeakHashMap<String, Object> params) {
PARAMS.putAll(params);
return this;
}
public final RestClientBuilder params(String key, Object value) {
PARAMS.put(key, value);
return this;
}
//文件上传
public final RestClientBuilder file(File file) {
this.mFile = file;
return this;
}
public final RestClientBuilder file(String file) {
this.mFile = new File(file);
return this;
}
public final RestClientBuilder raw(String raw) {
this.mBody = RequestBody.create(MediaType.parse("application/json;charset=UTF-8"), raw);
return this;
}
//不用写 set什么什么 ,直接简单明了写个success、写个raw、写个params,
// 中心突出,简洁明了
public final RestClientBuilder success(ISuccess iSuccess) {
this.mISuccess = iSuccess;
return this;
}
public final RestClientBuilder failure(IFailure iFailure) {
this.mIFailure = iFailure;
return this;
}
public final RestClientBuilder error(IError iError) {
this.mIError = iError;
return this;
}
public final RestClientBuilder onRequest(IRequest iRequest) {
this.mIRequest = iRequest;
return this;
}
//Loader
public final RestClientBuilder loader(Context context,LoaderStyle style) {
this.mContext = context;
this.mLoaderStyle = style;
return this;
}
public final RestClientBuilder loader(Context context) {
this.mContext = context;
this.mLoaderStyle = LoaderStyle.BallClipRotatePulseIndicator;
return this;
}
//文件下载
public final RestClientBuilder name(String name) {
this.mName = name;
return this;
}
public final RestClientBuilder dir(String dir) {
this.mDownloadDir = dir;
return this;
}
public final RestClientBuilder extension(String extension) {
this.mExtension = extension;
return this;
}
/**
* 最终方法,返回 构建组装 完毕的 RestClient
* @return
*/
public final RestClient build() {
return new RestClient(mUrl, PARAMS,
mDownloadDir, mExtension, mName,
mIRequest, mISuccess, mIFailure,
mIError, mBody,mFile, mContext,
mLoaderStyle);
}
}
------------------------------------------------------
/**
* <pre>
* desc : 使用建造者模式,构建 RestClient!!!
* 此类是建造者模式中,具体产品类部分
* </pre>
*/
public class RestClient {
//URL
private final String URL;
//参数
private static final WeakHashMap<String, Object> PARAMS = RestCreator.getParams();
//回调接口
private final IRequest REQUEST;
private final ISuccess SUCCESS;
private final IFailure FAILURE;
private final IError ERROR;
//文件下载用 变量,注意,一般!!传入了前两个就不用传入NAME,传入NAME了就不用传入前两个
private String DOWNLOAD_DIR;//指定 下载文件 在本地sd卡的 目录名
private String EXTENSION;//文件后缀名
private String NAME;//完整的文件名
//请求体
private final RequestBody BODY;
//Loader
private final LoaderStyle LOADER_STYLE;
private final Context CONTEXT;
//文件上传
private final File FILE;
public RestClient(String url,
Map<String, Object> params,
String downloadDir,
String extension,
String name,
IRequest request,
ISuccess success,
IFailure failure,
IError error,
RequestBody body,
File file,
Context context,
LoaderStyle loaderStyle) {
this.URL = url;
PARAMS.putAll(params);
this.DOWNLOAD_DIR = downloadDir;
this.EXTENSION = extension;
this.NAME = name;
this.REQUEST = request;
this.SUCCESS = success;
this.FAILURE = failure;
this.ERROR = error;
this.BODY = body;
this.FILE = file;
this.CONTEXT = context;
this.LOADER_STYLE = loaderStyle;
}
public static RestClientBuilder builder() {
return new RestClientBuilder();
}
//封装请求操作
private void request(HttpMethod method) {
//获取为 Retrofit 框架 准备的 接口对象实例!!!
final RestService service = RestCreator.getRestService();
//用来接请求操作
Call<String> call = null;
if (REQUEST != null) {
REQUEST.onRequestStart();
}
//展示 Loading!!(请求开始时)
// 对应的 关闭的话在 RequestCallBacks 中 实现(请求结束时关闭!!)
if (LOADER_STYLE != null) {
XiaoYunLoader.showLoading(CONTEXT, LOADER_STYLE);
} else {
XiaoYunLoader.showLoading(CONTEXT);
}
switch (method) {
case GET:
call = service.get(URL, PARAMS);
break;
case POST:
call = service.post(URL, PARAMS);
break;
case POST_RAW:
call = service.postRaw(URL, BODY);
break;
case PUT:
call = service.put(URL, PARAMS);
break;
case PUT_RAW:
call = service.putRaw(URL, BODY);
break;
case DELETE:
call = service.delete(URL, PARAMS);
break;
case UPLOAD:
final RequestBody requestBody =
RequestBody.create(MediaType.parse(MultipartBody.FORM.toString()),FILE);
//以 Form 的方式 提交
final MultipartBody.Part body =
MultipartBody.Part.createFormData("file", FILE.getName(),requestBody);
call = service.upload(URL, body);
break;
default:
break;
}
if (call != null) {
//如果 call不空,说明 需要进行请求的操作和参数 已经设定完毕
//这里是选enqueue() 进行 异步请求方式,执行请求
call.enqueue(getRequestCallback());
}
}
//返回一个 RequestCallBacks 实例
private Callback<String> getRequestCallback() {
return new RequestCallBacks(
REQUEST,
SUCCESS,
FAILURE,
ERROR,
LOADER_STYLE
);
}
/*
提供给外部调用的操作封装方法!!
所有调用 RestService 获取 Call 对象实例的 调用逻辑,以及 call 的请求执行逻辑,
都是在 RestClient 这里完成的,只有 download 的 RestService调用以及 call 的请求执行,
是跳转到 DownloadHandler 中完成的
*/
//增删改查,依次如下
public final void post() {
//BODY 或者 PARAMS 为不为空,就看 RestClientBuilder 构建时有没有配置;
// RestClientBuilder 构建时 有配置的 字段,则不空,否则默认为空
if (BODY == null) {
//如果 BODY 为空,即是 一般的 PARAMS post 方式
request(HttpMethod.POST);
} else {
//如果 BODY 不空,那只能是 post原始数据 BODY !!
if (!PARAMS.isEmpty()) {
//如果 BODY 不空,那只能是 post原始数据 BODY,
// 这种 post 情况的话,要求参数 PARAMS 必须为空!
//如果参数 PARAMS 不空
throw new RuntimeException("params must be null");
}
//如果参数 PARAMS 为空
request(HttpMethod.POST_RAW);
}
}
public final void delete() {
request(HttpMethod.DELETE);
}
public final void put() {
if (BODY == null) {
//如果 BODY 为空,即是 一般的 PARAMS put 方式
request(HttpMethod.PUT);
} else {
//如果 BODY 不空,那只能是 put原始数据 BODY !!
if (!PARAMS.isEmpty()) {
//如果 BODY 不空,那只能是 put原始数据 BODY,
// 这种 put 情况的话,要求参数 PARAMS 必须为空!
//如果参数 PARAMS 不空
throw new RuntimeException("params must be null");
}
//如果参数 PARAMS 为空
request(HttpMethod.PUT_RAW);
}
}
public final void get() {
request(HttpMethod.GET);
}
//文件上传
public final void upload() {
request(HttpMethod.UPLOAD);
}
//文件下载
public final void download() {
new DownloadHandler(URL, PARAMS,REQUEST, DOWNLOAD_DIR, EXTENSION, NAME,
SUCCESS, FAILURE, ERROR)
.handleDownload();
}
}
参考:
- 《Android源码设计模式解析与实战》
- 慕课网