一、引言

在Android开发中,采用Builder模式的代码随处可见,比如说Android系统对话框AlertDialog的使用或者是Android中的通知栏(Notification)的使用,又比如说在一些常用的第三方库中也随处可见其踪迹,比如说一些常用的网络请求库如OkHttp或者是retrofit,又或者是图片加载库Glide中也不缺乏它的应用。

为什么Builder模式在Android或是Java开发中这么火呢?因为它相较于构造函数或者是Get/Set方法,它的灵活性和封装性上都比较有优势。下面就通过具体的例子说明下Builder模式的具体使用方式,直接结合代码会更加直观。

二、类实例初始化需求的实现

首先提出我们的需求:

现在我们想要实例化一个类,如下所示,其中有些属性是必选的,而有些属性是可选的。如下所示:

  1. public class User {
  2. private final String mFirstName; //必选
  3. private final String mLastName; //必选
  4. private final String mGender; //可选
  5. private final int mAge; //可选
  6. private final String mPhoneNo; //可选
  7. }

方法一:

  1. **
  2. * 定义多个重载的构造函数实现
  3. * Created by DB on 2017/6/23.
  4. */
  5. public class User3 {
  6. private final String mFirstName;
  7. private final String mLastName;
  8. private final String mGender;
  9. private final int mAge;
  10. private final String mPhoneNo;
  11.  
  12. public User3(String mFirstName,String mLastName){
  13. this(mFirstName,mLastName,"");
  14. }
  15. public User3(String mFirstName,String mLastName,String mGender){
  16. this(mFirstName,mLastName,mGender,0);
  17. }
  18. public User3(String mFirstName,String mLastName,String mGender,int mAge){
  19. this(mFirstName,mLastName,mGender,mAge,"");
  20. }
  21.  
  22. public User3(String mFirstName, String mLastName, String mGender,int mAge,String mPhoneNo) {
  23. this.mFirstName = mFirstName;
  24. this.mLastName = mLastName;
  25. this.mGender = mGender;
  26. this.mAge = mAge;
  27. this.mPhoneNo = mPhoneNo;
  28. }
  29. }

在方法一中,我们使用了多个重载的构造函数来实现初始化不同属性值的需求,而可想而知,如果想要实现所有可能性的属性需求,会产生大量重载版本的构造函数,使得代码冗长且不好用。

方法二:

  1. /**
  2. * 采用get/set
  3. * Created by DB on 2017/6/23.
  4. */
  5. public class User4 {
  6.  
  7. private String mFirstName;
  8. private String mLastName;
  9. private String mGender;
  10. private int mAge;
  11. private String mPhoneNo;
  12.  
  13. public String getmFirstName() {
  14. return mFirstName;
  15. }
  16.  
  17. public void setmFirstName(String mFirstName) {
  18. this.mFirstName = mFirstName;
  19. }
  20.  
  21. public String getmLastName() {
  22. return mLastName;
  23. }
  24.  
  25. public void setmLastName(String mLastName) {
  26. this.mLastName = mLastName;
  27. }
  28.  
  29. public String getmGender() {
  30. return mGender;
  31. }
  32.  
  33. public void setmGender(String mGender) {
  34. this.mGender = mGender;
  35. }
  36.  
  37. public int getmAge() {
  38. return mAge;
  39. }
  40.  
  41. public void setmAge(int mAge) {
  42. this.mAge = mAge;
  43. }
  44.  
  45. public String getmPhoneNo() {
  46. return mPhoneNo;
  47. }
  48.  
  49. public void setmPhoneNo(String mPhoneNo) {
  50. this.mPhoneNo = mPhoneNo;
  51. }
  52. }

在方法2中,我们使用了get和set方法来完成对属性值的设定和读取,如果是这样的话,就需要将属性值的final关键字去掉,这也就意味着这个类的实例不再是不可变的,与原先的需求有所出入,并且这个类的实例状态不联系,比如说如果你想创建一个同时具有几个属性值的类实例时,只有当最后那个实例属性的set函数被调用时,该实例才具有完整联系的状态,这意味着调用者可能会看到该类实例的不连续状态。

方法3:

终于轮到了我们的Builder模式大展身手了,这里我们使用的Builder模式中的变种模式,具体代码如下:

  1. /**
  2. * Created by DB on 2017/6/23.
  3. */
  4. public class User {
  5. private final String mFirstName; //必选
  6. private final String mLastName; //必选
  7. private final String mGender; //可选
  8. private final int mAge; //可选
  9. private final String mPhoneNo; //可选
  10.  
  11. //构造函数为私有的,调用者不能直接实例化该类,需要通过builder去实现实例化
  12. private User(UserBuilder builder){
  13. mFirstName=builder.firstName;
  14. mLastName=builder.lastName;
  15. mGender=builder.gender;
  16. mAge=builder.age;
  17. mPhoneNo=builder.phoneNo;
  18.  
  19. }
  20. //仅提供get方法,而不提供set方法,确保User类的不可变性
  21. public String getmFirstName() {
  22. return mFirstName;
  23. }
  24.  
  25. public String getmLastName() {
  26. return mLastName;
  27. }
  28.  
  29. public String getmGender() {
  30. return mGender;
  31. }
  32.  
  33. public int getmAge() {
  34. return mAge;
  35. }
  36.  
  37. public String getmPhoneNo() {
  38. return mPhoneNo;
  39. }
  40.  
  41. //构造函数只接受必选的属性值作为参数,并且只有必选的属性值设置为final,以确保其在构造函数中设置
  42. public static class UserBuilder {
  43. private final String firstName;
  44. private final String lastName;
  45. private String gender;
  46. private int age;
  47. private String phoneNo;
  48.  
  49. public UserBuilder(String firstName,String lastName){
  50. this.firstName=firstName;
  51. this.lastName=lastName;
  52. }
  53. public UserBuilder gender(String gender){
  54. this.gender=gender;
  55. return this;
  56. }
  57. public UserBuilder age(int age){
  58. this.age=age;
  59. return this;
  60. }
  61. public UserBuilder phoneNo(String phoneNo){
  62. this.phoneNo=phoneNo;
  63. return this;
  64. }
  65.  
  66. public User build(){
  67. return new User(this);
  68. }
  69. }
  70.  
  71. public static void main(String[] args){
  72. User user = new UserBuilder("ZHB","Pignet")
  73. .gender("male")
  74. .age(25)
  75. .phoneNo("1234456")
  76. .build();
  77. System.out.println(user.getmFirstName()+" "+user.getmLastName());
  78. System.out.println(user.getmAge()+" "+user.getmGender());
  79. }
  80. }

在这里我们加入了一个测试用例,其结果如下所示:

从上述代码可以看到我们熟悉的链式编程,这边我们将必选的属性值作为参数传到Builder的构造函数中,并且为final,其他的属性值则通过各自的方法实现设置,最后在build方法中新建一个类的实例对象,并返回。

三、Builder模式的优缺点

优点:将类实例的初始化实现分为构建和表示两部分,同时,也将类实例的初始化从目标类中隔离出来(集中到了一个内部类中),避免了过度的Set方法,同时采用调用链式的实现,使得代码更加简洁明了。

缺点:需要编写很多的样板代码,我们需要在内部类UserBuilder中重复外部类User的属性定义

四、使用插件自动生成代码

为了规避掉Builder模式中的缺点,我们可以在Android Studio中(Intelligent IDEA)通过安装名为InnerBuilder的插件来简化Builder模式的创建过程。

来看看它的生成效果

  1. /**
  2. * Created by DB on 2017/6/23.
  3. */
  4. public class User2 {
  5. private final String mFirstName;
  6. private final String mLastName;
  7. private final String mGender;
  8. private final int mAge;
  9. private final String mPhoneNo;
  10.  
  11. private User2(Builder builder) {
  12. mFirstName = builder.mFirstName;
  13. mLastName = builder.mLastName;
  14. mGender = builder.mGender;
  15. mAge = builder.mAge;
  16. mPhoneNo = builder.mPhoneNo;
  17. }
  18.  
  19. public static final class Builder {
  20. private String mFirstName;
  21. private String mLastName;
  22. private String mGender;
  23. private int mAge;
  24. private String mPhoneNo;
  25.  
  26. public Builder() {
  27. }
  28.  
  29. public Builder mFirstName(String val) {
  30. mFirstName = val;
  31. return this;
  32. }
  33.  
  34. public Builder mLastName(String val) {
  35. mLastName = val;
  36. return this;
  37. }
  38.  
  39. public Builder mGender(String val) {
  40. mGender = val;
  41. return this;
  42. }
  43.  
  44. public Builder mAge(int val) {
  45. mAge = val;
  46. return this;
  47. }
  48.  
  49. public Builder mPhoneNo(String val) {
  50. mPhoneNo = val;
  51. return this;
  52. }
  53.  
  54. public User2 build() {
  55. return new User2(this);
  56. }
  57. }
  58.  
  59. }

对比前面的例子,我们不难发现这一段自动生成的代码和我们前面写的代码有一点点不同,我们可以在此基础上根据实际的需求进行修改(如前面的必选项采用构造函数来初始化部分)

Builder模式详解及其在Android开发中的应用的更多相关文章

  1. android开发之动画的详解 整理资料 Android开发程序小冰整理

    /** * 作者:David Zheng on 2015/11/7 15:38 * *  网站:http://www.93sec.cc * *  微博:http://weibo.com/mcxiaob ...

  2. Extjs MVC开发模式详解

    Extjs MVC开发模式详解   在JS的开发过程中,大规模的JS脚本难以组织和维护,这一直是困扰前端开发人员的头等问题.Extjs为了解决这种问题,在Extjs 4.x版本中引入了MVC开发模式, ...

  3. 设计模式相关面试问题-Builder基础详解与代码解读

    java的builder模式详解: 概念:建造者模式是较为复杂的创建型模式,它将客户端与多含多个组成部分(或部件)的复杂对象的创建过程分离. 使用场景:当构造一个对象需要很多参数的时候,并且参数的个数 ...

  4. ext.js的mvc开发模式详解

    ext.js的mvc开发模式详解和环境配置 在JS的开发过程中,大规模的JS脚本难以组织和维护,这一直是困扰前端开发人员的头等问题.Extjs为了解决这种问题,在Extjs 4.x版本中引入了MVC开 ...

  5. Android 订阅-发布者模式-详解

    1.概念简述 Android 简称观察者模式, GoF说道:Observer模式的意图是“定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新”. 有 ...

  6. 转: Android开发中的MVP架构详解(附加链接比较不错)

    转: http://www.codeceo.com/article/android-mvp-artch.html 最近越来越多的人开始谈论架构.我周围的同事和工程师也是如此.尽管我还不是特别深入理解M ...

  7. Android 开发中的屏幕适配技术详解

    本文主要介绍Android开发中比较头疼繁琐的一个问题-屏幕适配问题.主要从适配原因.基本核心概念.适配方法等方面介详细 介绍从而是的深入或者进一步对Android屏幕适配技术的掌握和理解. 真题园网 ...

  8. android开发之eclipse调试debug模式详解

     之前我写了一个相关的帖子,但是今天看了一个还是写的比我详细,于是我拿过来和大家分享. 1.在程序中添加一个断点 如果所示:在Eclipse中添加了一个程序断点 在Eclipse中一共有三种添加断 ...

  9. Android开发中无处不在的设计模式——动态代理模式

    继续更新设计模式系列.写这个模式的主要原因是近期看到了动态代理的代码. 先来回想一下前5个模式: - Android开发中无处不在的设计模式--单例模式 - Android开发中无处不在的设计模式-- ...

随机推荐

  1. [ABP实战开源项目]---ABP实时服务-通知系统.发布模式

    简介 在ABP中,提供了通知服务.它是一个基于实时通知的基础设施.分为订阅模式和发布模式. 本次会在项目中使用发布模式来演示一个用户注册后,收到的欢迎信息. 发布模式 首先我们在领域层建立" ...

  2. tpcc-mysql的使用

    1.tpcc-mysql的业务逻辑及其相关的几个表作用:      New-Order:新订单,一次完整的订单事务,几乎涉及到全部表      Payment:支付,主要对应 orders.histo ...

  3. PHP实现类似于MySQ L的group by 效果

    简单版本:只用于获取组数据 其实就是将键值变成键,遍历的时候键相同的会覆盖 $res[$v['country']] [] 可以 = 1,无关紧要

  4. 【SoDiaoEditor电子病历编辑器】阶段性更新啦

    转眼距离上一次v2正式发布已经过去一个半月了.github期间不定期push了二十几次,同时感谢分布在广州.福建.上海.北京的一众小伙伴,正是你们给出的建议,才让SoDiaoEditor不断完善. 我 ...

  5. windows下命令行cmder工具

    windows下系统自带的命令行工具,实在是太丑了,输入命令后,有时排版乱七八糟,而且使用惯liunx系统的命令后,实在是不能够接受,这么蹩脚的工具:为此我给大家推荐一款实用的开源工具cmder 下载 ...

  6. php中for循环的应用

    for 循环是 PHP 中最复杂的循环结构.它的行为和 C 语言的相似.在PHP中使用的是执行相同的代码集的次数. for 循环的语法是: for (expr1; expr2; expr3)state ...

  7. .nomedia文件的作用

    .nomedia文件的作用:应用中的图片不被系统图库扫描. 一般开发的应用中会缓存一些图片到本地,不想让系统图库扫描到应用的图片或者不想对用户浏览图片造成影响. .nomedia文件放在任何一个文件夹 ...

  8. 微服务框架下的思维变化-OSS.Core基础思路

    如今框架两字已经烂大街了,xx公司架构设计随处可见,不过大多看个热闹,这些框架如何来的,细节又是如何思考的,相互之间的隔离依据又是什么...相信很多朋友应该依然存在自己的疑惑,特别是越来越火热的微服务 ...

  9. elasticsearch 不能通过9200端口访问

    修改配置文件 config/elasticsearch.yml network.host: 0.0.0.0

  10. 读 Zepto 源码之集合元素查找

    这篇依然是跟 dom 相关的方法,侧重点是跟集合元素查找相关的方法. 读Zepto源码系列文章已经放到了github上,欢迎star: reading-zepto 源码版本 本文阅读的源码为 zept ...