一、介绍

Builder模式是一步一步创建一个复杂对象的创建型模式,它允许用户在不知道内部构建细节的情况下,可以更精细的控制对象的构造流程。该模式是为了将构建复杂对象的过程和它的部件解耦,使得构建过程和部件的表示隔离开来。

因为一个复杂的对象有很多大量组成部分,例如车,有车轮、方向盘、发动机,还有各种小零件等,如何将这些部件装配成一辆汽车,这个装配过程很漫长,也很复杂,对于这种情况,为了在构建过程中对外部隐藏实现细节,就可以使用Builder模式将部件和组装过程分离,使得构建过程和部件都可以自由扩展,两者之间的耦合也降到最低。

二、定义

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

三、使用场景

(1)相同的方法,不同的执行顺序,产生不同的事件结果时。

(2)多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时。

(3)产品类非常复杂,或者产品类中的调用顺序不同产生了不同的作用,这个时候使用建造者模式非常合适。

(4)当初始化一个对象特别复杂,如参数多,且很多参数都具有默认值时。

四、Builder模式的UML类图

角色介绍:

  • Product产品类——产品的抽象类;

  • Builder——抽象Builder类,规范产品的组建,一般是由子类实现具体的组建过程;

  • ConcreateBuilder——具体的Builder类;

  • Director——统一组装过程;

五、Builder模式的简单实现

计算机的组装过程较为复杂,并且组装顺序是不固定的,为了易于理解,我们把计算机的组装过程简化为构建主机、设置操作系统、设置显示器3个部分,然后通过Director和具体的Builder来构建计算机对象。

示例代码:

/*** 计算机抽象类,即Product角色*/
public abstract class Computer {protected String mBoard;protected String mDisplay;protected String mOS;protected Computer(){}/*** 设置主板* @param board*/public void setBoard(String board){this.mBoard = board;}/*** 设置显示器* @param display*/public void setDisplay(String display){this.mDisplay = display;}/*** 设置操作系统*/public abstract void setOS();@Overridepublic String toString(){return "Computer [mBoard=" + mBoard + ", mDisplay=" + mDisplay + ", mOS=" + mOS + "]";}
}
/*** 具体的Computer类,Macbook*/
public class Macbook extends Computer {protected Macbook(){}@Overridepublic void setOS() {mOS = "Mac OS X 10";}
}
/*** 抽象Builder类*/
public abstract class Builder {/*** 设置主机* @param board*/public abstract void buildBoard(String board);/*** 设置显示器* @param display*/public abstract void buildDisplay(String display);/*** 设置操作系统*/public abstract void buildOS();/*** 创建Computer* @return*/public abstract Computer create();
}
/*** 具体的Builder类,MacbookBuilder*/
public class MacbookBuilder extends Builder {private Computer mComputer = new Macbook();@Overridepublic void buildBoard(String board) {mComputer.setBoard(board);}@Overridepublic void buildDisplay(String display) {mComputer.setDisplay(display);}@Overridepublic void buildOS() {mComputer.setOS();}@Overridepublic Computer create() {return mComputer;}
}
/*** Director类,负责构造Computer*/
public class Director {Builder mBuilder = null;public Director(Builder builder){mBuilder = builder;}/*** 构建对象* @param board 主板* @param display 显示器*/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();//DirectorDirector pcDirector = new Director(builder);//封装构建过程pcDirector.construct("英特尔主板","Retina显示器");//构建计算机,输出相关信息System.out.println("Computer Info : " + builder.create().toString());}
}

输出结果:

Computer Info : Computer [mBoard=英特尔主板, mDisplay=Retina显示器, mOS=Mac OS X 10]

上述示例中,通过具体的MacbookBuilder来构建Macbook对象,而Director封装了构建复杂产品对象的过程,对外隐藏构建细节。Builder与Director一起将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的对象。

值得注意的是,在现实的开发过程中,Director角色经常会被省略。而直接使用一个Builder来进行对象的组装,这个Builder通常为链式调用,它的关键点是每个setter方法都返回自身,也就是return this,这样就使得setter方法可以链式调用,代码大致如下:

new TestBuilder().setA("A").create();

通过这种形式不仅去除了Director角色,整个结构也更加简单,也能对Product对象的组装过程有更精细的控制。

六、Builder模式变种——链式调用

示例代码:

public class User {private final String name;         //必选private final String cardID;       //必选private final int age;             //可选private final String address;      //可选private final String phone;        //可选private User(UserBuilder userBuilder){this.name=userBuilder.name;this.cardID=userBuilder.cardID;this.age=userBuilder.age;this.address=userBuilder.address;this.phone=userBuilder.phone;}public String getName() {return name;}public String getCardID() {return cardID;}public int getAge() {return age;}public String getAddress() {return address;}public String getPhone() {return phone;}public static class UserBuilder{private final String name;private final String cardID;private int age;private String address;private String phone;public UserBuilder(String name,String cardID){this.name=name;this.cardID=cardID;}public UserBuilder age(int age){this.age=age;return this;}public UserBuilder address(String address){this.address=address;return this;}public UserBuilder phone(String phone){this.phone=phone;return this;}public User build(){return new User(this);}}
}

需要注意的点:

  • User类的构造方法是私有的,调用者不能直接创建User对象。

  • User类的属性都是不可变的。所有的属性都添加了final修饰符,并且在 构造方法中设置了值。并且,对外只提供getters方法。

  • Builder的内部类构造方法中只接收必传的参数,并且该必传的参数使用了final修饰符。

调用方式:

new User.UserBuilder("Jack","10086").age(25).address("GuangZhou").phone("13800138000").build();

相比起前面通过构造函数和setter/getter方法两种方式,可读性更强。唯一可能存在的问题就是会产生多余的Builder对象,消耗内存。然而大多数情况下我们的Builder内部类使用的是静态修饰的(static),所以这个问题也没多大关系。

关于线程安全

Builder模式是非线程安全的,如果要在Builder内部类中检查一个参数的合法性,必需要在对象创建完成之后再检查

正确示例:

public User build() {User user = new user(this);if (user.getAge() > 120) {throw new IllegalStateException(“Age out of range”); // 线程安全}return user;
}

错误示例:

public User build() {if (age > 120) {throw new IllegalStateException(“Age out of range”); // 非线程安全}return new User(this);
}

七、用到Builder模式的例子

1、Android中的AlertDialog.Builder

private void showDialog(){AlertDialog.Builder builder=new AlertDialog.Builder(context);builder.setIcon(R.drawable.icon);builder.setTitle("Title");builder.setMessage("Message");builder.setPositiveButton("Button1", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {//TODO}});builder.setNegativeButton("Button2", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {//TODO}});builder.create().show();
}

2、OkHttp中OkHttpClient的创建

OkHttpClient  okHttpClient = new OkHttpClient.Builder().cache(getCache()) .addInterceptor(new HttpCacheInterceptor()).addInterceptor(new LogInterceptor()).addNetworkInterceptor(new HttpRequestInterceptor()) .build();

3、Retrofit中Retrofit对象的创建

Retrofit retrofit = new Retrofit.Builder().client(createOkHttp()).addConverterFactory(GsonConverterFactory.create()).addCallAdapterFactory(RxJavaCallAdapterFactory.create()).baseUrl(BASE_URL).build();

可见在实际使用中,均省略掉了Director角色,在很多框架源码中,涉及到Builder模式时,大多都不是经典GOF的Builder模式,而是选择了结构更加简单的后者。

八、优缺点

优点:

  • 良好的封装性,使得客户端不需要知道产品内部实现的细节

  • 建造者独立,扩展性强

缺点:

  • 产生多余的Builder对象、Director对象,消耗内存

参考资料

《Android源码设计模式与解析实战》

设计模式之Builder模式

Android设计模式之——Builder模式相关推荐

  1. Android常用设计模式之Builder模式理解

    Android常用设计模式之Builder模式 1 单例模式 2 Builder模式 Builder模式的应用场景 总结 1 单例模式 单例模式之前有详细的介绍,可移步到链接: 常见的单例模式及其特点 ...

  2. Java 设计模式之Builder模式

    设计模式系列 创建型设计模式 Java 设计模式之单例模式 Java 设计模式之静态工厂方法模式 Java 设计模式之工厂方法模式 Java 设计模式之抽象工厂模式 Java 设计模式之Builder ...

  3. Java设计模式之Builder模式

    Java设计模式之Builder模式 Java设计模式之Builder模式 简介 适用性 用LOL的出装备的顺序为例 基本实现代码 BasePerson的实现 DeMaXiYa的实现 QiTianDa ...

  4. 「设计模式(六) - Builder模式」

    「设计模式(六) - Builder模式」 一.可定制化的 电脑的组装在生活中并不陌生,大家都有电脑,当然需求不一样配置也不一样.以Macbook Pro为例,像UI设计对图像模块GPU要求比较高,跑 ...

  5. Android设计模式之建造者模式builder

    今天小编来说一下Android中的一种设计模式--建造者模式Builder 绪论: 那么对于Android初级来说,Builder设计模式可能在我们开发中用过的很少,但是我们可能见过,我们经常用的Al ...

  6. Android常考问题(8)-设计模式:Builder模式(顺带学习了一下String的比较和final)

    今天的主要目的是学习设计模式中的Builder模式.由于java基础不牢固,在学习过程中要回过头去学习java内容,因此凑成了这样一篇驳杂的文章. Builder模式 首先是Builder设计模式的作 ...

  7. 【设计模式】Builder模式

    一.前言 设计模式系列(参考资料:<Android源码设计模式解析与实战>--何红辉.关爱明) 单例模式 Builder模式 原型模式 二.介绍 我们开发中偶尔会遇到一些需要设置10个或以 ...

  8. 码农小汪-设计模式之-Builder模式

    建造者模式 将一个复杂的对象的构建与它的表示分离,使得同样构建的过程中可以创建不同的表示.这个话语看起来,好累啊!真心很难理解. 下面是它的UML图: 抽象建造者角色(Builder):为创建一个Pr ...

  9. Android设计模式之——访问者模式

    一.介绍 访问者模式是一种将数据操作与数据结构分离的设计模式,它是<设计模式>中23种设计模式中最复杂的一个,但它的使用频率并不高,正如<设计模式>的作者GOF对访问者模式的描 ...

最新文章

  1. 10061 mysql,Navicat无法连接到MySQL server的10061错误
  2. 无法使用BIPublisher开发报表
  3. docker lamp php7,如何用docker安装lamp
  4. python显示外部命令_Python 执行外部指令
  5. HtmlAgilityPack的简单使用
  6. Google Guava并发– ListenableFuture
  7. 【渝粤题库】陕西师范大学191203 法理学导论 作业
  8. android handler封装_Handler都没搞懂,你拿什么去跳槽啊?!
  9. linux下生成coredump文件
  10. Android获取设备隐私 忽略6.0权限管理
  11. java学习是什么_学习JAVA有什么作用?
  12. bug 执行nrm 报错internal/validators.js:121 throw new ERR_INVALID_ARG_TYPE(name, ‘string‘, value);
  13. Hadoop的基本结构介绍(原创)
  14. iMazing的付费功能与免费功能对比
  15. pkg_resources.ContextualVersionConflict: (pyasn1 0.1.9 (c:\users\dell\anaconda3\lib\site-packages)
  16. MATLAB数值微积分与方程求解
  17. 人性的弱点【我要喜欢你】
  18. 国内好的破解软件下载站
  19. 爬虫学习之17:爬取拉勾网网招聘信息(异步加载+Cookie模拟登陆)
  20. 纯干货分享,2021年阿里巴巴社招面试题总结,本人上周已成功入职!

热门文章

  1. linux串口编程实例_Linux 网络编程——原始套接字实例:发送 UDP 数据包
  2. LSGO软件技术团队2015~2016学年第六周(1005~1011)总结
  3. 【转】Windows Server 2012 R2 双网卡绑定
  4. 第八节:Task的各类TaskTResult返回值以及通用线程的异常处理方案
  5. QStackedWidget实现自适应紧凑布局
  6. mysql 索引 lt =_MySQL索引相关
  7. 帆软单点登录_电子表格FineReport教程:[20]CAS单点登录
  8. CCNA-第三篇-OSI模型-上
  9. 【牛客 - 330I】Applese 的回文串(结论题,类似编辑距离,dp)
  10. 【CodeForces - 298C】Parity Game (思维,有坑)