Builder模式详解及其在Android开发中的应用
一、引言
在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开发中的应用的更多相关文章
- android开发之动画的详解 整理资料 Android开发程序小冰整理
/** * 作者:David Zheng on 2015/11/7 15:38 * * 网站:http://www.93sec.cc * * 微博:http://weibo.com/mcxiaob ...
- Extjs MVC开发模式详解
Extjs MVC开发模式详解 在JS的开发过程中,大规模的JS脚本难以组织和维护,这一直是困扰前端开发人员的头等问题.Extjs为了解决这种问题,在Extjs 4.x版本中引入了MVC开发模式, ...
- 设计模式相关面试问题-Builder基础详解与代码解读
java的builder模式详解: 概念:建造者模式是较为复杂的创建型模式,它将客户端与多含多个组成部分(或部件)的复杂对象的创建过程分离. 使用场景:当构造一个对象需要很多参数的时候,并且参数的个数 ...
- ext.js的mvc开发模式详解
ext.js的mvc开发模式详解和环境配置 在JS的开发过程中,大规模的JS脚本难以组织和维护,这一直是困扰前端开发人员的头等问题.Extjs为了解决这种问题,在Extjs 4.x版本中引入了MVC开 ...
- Android 订阅-发布者模式-详解
1.概念简述 Android 简称观察者模式, GoF说道:Observer模式的意图是“定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新”. 有 ...
- 转: Android开发中的MVP架构详解(附加链接比较不错)
转: http://www.codeceo.com/article/android-mvp-artch.html 最近越来越多的人开始谈论架构.我周围的同事和工程师也是如此.尽管我还不是特别深入理解M ...
- Android 开发中的屏幕适配技术详解
本文主要介绍Android开发中比较头疼繁琐的一个问题-屏幕适配问题.主要从适配原因.基本核心概念.适配方法等方面介详细 介绍从而是的深入或者进一步对Android屏幕适配技术的掌握和理解. 真题园网 ...
- android开发之eclipse调试debug模式详解
之前我写了一个相关的帖子,但是今天看了一个还是写的比我详细,于是我拿过来和大家分享. 1.在程序中添加一个断点 如果所示:在Eclipse中添加了一个程序断点 在Eclipse中一共有三种添加断 ...
- Android开发中无处不在的设计模式——动态代理模式
继续更新设计模式系列.写这个模式的主要原因是近期看到了动态代理的代码. 先来回想一下前5个模式: - Android开发中无处不在的设计模式--单例模式 - Android开发中无处不在的设计模式-- ...
随机推荐
- nodejs querystring踩坑笔记----只能用于表单提交
API中的实例: var http = require('http'); var querystring = require('querystring'); var postData = querys ...
- Apache localhost和局域网ip地址访问
今天忍无可忍重装了公司的电脑,所以把开发工具也都重新装一下. 安装wamp,localhost和局域网ip地址无法访问. 在C:\Windows\System32\drivers\etc\hosts文 ...
- hdu 4552 怪盗基德的挑战书
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4552 思路: 其实可以用KMP+DP解决 我这里给出后缀数组的解题方法 思路: 初始化ans=n(n为 ...
- PS不能存储,因为程序错误
当PS中遇到不能存储文件,因为程序错误时,可以这样: http://www.zcool.com.cn/article/ZMTgwOTQw.html
- weblogic java.lang.OutOfMemoryError: PermGen space 问题解决方法
文章转自:http://blog.csdn.net/cuihaiyang/article/details/6679735 最近安装了WebLogic10.3.4,测试在weblogic上部署项目,没过 ...
- Java学习笔记——排序算法之简单排序
男儿何不带吴钩,收取关山五十州.请君暂上凌烟阁,若个书生万户侯? --南园十三首 三种排序法: 1.冒泡法 2.简单选择法 3.直接插入法 上代码: 1.冒泡排序 public class Bub ...
- JS执行效率与性能提升方案
如果是追加字符串,最好使用s+=anotherStr操作,而不是要使用s=s+anotherStr.如果要连接多个字符串,应该少使用+=,如 s+=a;s+=b;s+=c;应该写成s+=a + b + ...
- 机器学习:从编程的角度理解BP神经网络
1.简介(只是简单介绍下理论内容帮助理解下面的代码,如果自己写代码实现此理论不够) 1) BP神经网络是一种多层网络算法,其核心是反向传播误差,即: 使用梯度下降法(或其他算法),通过反向传播来不断调 ...
- 初识Java反射
要详细的了解Java反射,就得要了解Java的类加载以及何为运行时动态加载等等概念.本文抛开其余概念,简单介绍Java反射,详细介绍会在以后有一个系统而全面的认识过后展开. 反射是Java被视为动态语 ...
- 当一个JavaScripter初次进入PHP的世界,他将看到这样的风景
本文将从以下11点介绍javascript和PHP在基础语法和基本操作上的异同: 1.数据类型的异同 2.常量和变量的定义的不同,字符串连接运算符不同 3.对象的创建方法的不同 4.PHP与JS在变 ...