一、引言

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

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

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

首先提出我们的需求:

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

public class User {
private final String mFirstName; //必选
private final String mLastName; //必选
private final String mGender; //可选
private final int mAge; //可选
private final String mPhoneNo; //可选
}

方法一:

**
* 定义多个重载的构造函数实现
* Created by DB on 2017/6/23.
*/
public class User3 {
private final String mFirstName;
private final String mLastName;
private final String mGender;
private final int mAge;
private final String mPhoneNo; public User3(String mFirstName,String mLastName){
this(mFirstName,mLastName,"");
}
public User3(String mFirstName,String mLastName,String mGender){
this(mFirstName,mLastName,mGender,0);
}
public User3(String mFirstName,String mLastName,String mGender,int mAge){
this(mFirstName,mLastName,mGender,mAge,"");
} public User3(String mFirstName, String mLastName, String mGender,int mAge,String mPhoneNo) {
this.mFirstName = mFirstName;
this.mLastName = mLastName;
this.mGender = mGender;
this.mAge = mAge;
this.mPhoneNo = mPhoneNo;
}
}

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

方法二:

/**
* 采用get/set
* Created by DB on 2017/6/23.
*/
public class User4 { private String mFirstName;
private String mLastName;
private String mGender;
private int mAge;
private String mPhoneNo; public String getmFirstName() {
return mFirstName;
} public void setmFirstName(String mFirstName) {
this.mFirstName = mFirstName;
} public String getmLastName() {
return mLastName;
} public void setmLastName(String mLastName) {
this.mLastName = mLastName;
} public String getmGender() {
return mGender;
} public void setmGender(String mGender) {
this.mGender = mGender;
} public int getmAge() {
return mAge;
} public void setmAge(int mAge) {
this.mAge = mAge;
} public String getmPhoneNo() {
return mPhoneNo;
} public void setmPhoneNo(String mPhoneNo) {
this.mPhoneNo = mPhoneNo;
}
}

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

方法3:

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

/**
* Created by DB on 2017/6/23.
*/
public class User {
private final String mFirstName; //必选
private final String mLastName; //必选
private final String mGender; //可选
private final int mAge; //可选
private final String mPhoneNo; //可选 //构造函数为私有的,调用者不能直接实例化该类,需要通过builder去实现实例化
private User(UserBuilder builder){
mFirstName=builder.firstName;
mLastName=builder.lastName;
mGender=builder.gender;
mAge=builder.age;
mPhoneNo=builder.phoneNo; }
//仅提供get方法,而不提供set方法,确保User类的不可变性
public String getmFirstName() {
return mFirstName;
} public String getmLastName() {
return mLastName;
} public String getmGender() {
return mGender;
} public int getmAge() {
return mAge;
} public String getmPhoneNo() {
return mPhoneNo;
} //构造函数只接受必选的属性值作为参数,并且只有必选的属性值设置为final,以确保其在构造函数中设置
public static class UserBuilder {
private final String firstName;
private final String lastName;
private String gender;
private int age;
private String phoneNo; public UserBuilder(String firstName,String lastName){
this.firstName=firstName;
this.lastName=lastName;
}
public UserBuilder gender(String gender){
this.gender=gender;
return this;
}
public UserBuilder age(int age){
this.age=age;
return this;
}
public UserBuilder phoneNo(String phoneNo){
this.phoneNo=phoneNo;
return this;
} public User build(){
return new User(this);
}
} public static void main(String[] args){
User user = new UserBuilder("ZHB","Pignet")
.gender("male")
.age(25)
.phoneNo("1234456")
.build();
System.out.println(user.getmFirstName()+" "+user.getmLastName());
System.out.println(user.getmAge()+" "+user.getmGender());
}
}

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

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

三、Builder模式的优缺点

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

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

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

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

来看看它的生成效果

/**
* Created by DB on 2017/6/23.
*/
public class User2 {
private final String mFirstName;
private final String mLastName;
private final String mGender;
private final int mAge;
private final String mPhoneNo; private User2(Builder builder) {
mFirstName = builder.mFirstName;
mLastName = builder.mLastName;
mGender = builder.mGender;
mAge = builder.mAge;
mPhoneNo = builder.mPhoneNo;
} public static final class Builder {
private String mFirstName;
private String mLastName;
private String mGender;
private int mAge;
private String mPhoneNo; public Builder() {
} public Builder mFirstName(String val) {
mFirstName = val;
return this;
} public Builder mLastName(String val) {
mLastName = val;
return this;
} public Builder mGender(String val) {
mGender = val;
return this;
} public Builder mAge(int val) {
mAge = val;
return this;
} public Builder mPhoneNo(String val) {
mPhoneNo = val;
return this;
} public User2 build() {
return new User2(this);
}
} }

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

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. java:Comparator比较器

    /*Comparator是java.util包里的一个接口,使用时应该实现导入相应的包, *再写一个实现了Comparator接口的类,并复写int compare(Object o)方法, *写出你 ...

  2. JS中作用域

    var scope = 'global'; var f = function() { console.log(scope); // 输出 undefined var scope = 'f'; } f( ...

  3. HAProxy的三种不同类型配置方案

    haproxy是一款功能强大.灵活好用反向代理软件,提供了高可用.负载均衡.后端服务器代理的功能,它在7层负载均衡方面的功能很强大(支持 cookie track, header rewrite等等) ...

  4. 个人对于epollhup的理解

    1.原因 由于最近对于异步connect函数的测试,发现提前将一个套接字加入epoll监听队列会不断爆出epollhup事件 2.示例 ........  iEpoll = epoll_create( ...

  5. Java 脚本化编程指南

    Java 脚本化编程指南 Java脚本化API为谁准备? 脚本语言的一些有用的特性是: 方便:大多数脚本语言都是动态类型的.您通常可以创建新的变量,而不声明变量类型,并且您可以重用变量来存储不同类型的 ...

  6. 3、J2EE学习推荐书籍

    3.J2EE学习推荐书籍       J2EE的学习应该循序渐进,一本好书会很快上手和深入.在学习J2EE之前,应该学好SQL,基本上,程序设计都会跟数据库打交道.如果SQL没学好,就如同房子没有基脚 ...

  7. sublime比较好用的插件

    emmet, markdown preview, package Control, SFTP, Anaconda

  8. 3.Java 加解密技术系列之 SHA

    Java 加解密技术系列之 SHA 序 背景 正文 SHA-1 与 MD5 的比较 代码实现 结束语 序 上一篇文章中介绍了基本的单向加密算法 — — MD5,也大致的说了说它实现的原理.这篇文章继续 ...

  9. (中级篇 NettyNIO编解码开发)第八章-Google Protobuf 编解码-2

    8.1.2    Protobuf编解码开发 Protobuf的类库使用比较简单,下面我们就通过对SubscrjbeReqProto进行编解码来介绍Protobuf的使用. 8-1    Protob ...

  10. ASP.NET Core:使用EntityFrameworkCore操作MySql来丰富仓储模块

    概述 上一篇简单介绍了Dapper的基本用法,数据的存储为SqlServer.那么这一篇就记录一下使用EFCore来操作MySql的一些方式,这种模式比较适合那种一个项目中需要操作多种数据库的模式.不 ...