Builder模式在Java中的应用
在设计模式中对Builder模式的定义是用于构建复杂对象的一种模式,所构建的对象往往需要多步初始化或赋值才能完成。那么,在实际的开发过程中,我们哪些地方适合用到Builder模式呢?其中使用Builder模式来替代多参数构造函数是一个比较好的实践法则。
我们常常会面临编写一个这样的实现类(假设类名叫DoDoContact),这个类拥有多个构造函数,
DoDoContact(String name);
DoDoContact(String name, int age);
DoDoContact(String name, int age, String address);
DoDoContact(String name, int age, String address, int cardID);
这样一系列的构造函数主要目的就是为了提供更多的客户调用选择,以处理不同的构造请求。这种方法很常见,也很有效力,但是它的缺点也很多。类的作者不得不书写多种参数组合的构造函数,而且其中还需要设置默认参数值,这是一个需要细心而又枯燥的工作。其次,这样的构造函数灵活性也不高,而且在调用时你不得不提供一些没有意义的参数值,例如,DoDoContact("Ace", -1, "SH"),显然年龄为负数没有意义,但是你又不的不这样做,得以符合Java的规范。如果这样的代码发布后,后面的维护者就会很头痛,因为他根本不知道这个-1是什么含义。对于这样的情况,就非常适合使用Builder模式。Builder模式的要点就是通过一个代理来完成对象的构建过程。这个代理职责就是完成构建的各个步骤,同时它也是易扩展的。下面是改写自Effective Java里面的一段代码:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
public class DoDoContact { private final int age; private final int safeID; private final String name; private final String address; public int getAge() { return age; } public int getSafeID() { return safeID; } public String getName() { return name; } public String getAddress() { return address; } public static class Builder { private int age = 0; private int safeID = 0; private String name = null; private String address = null; |
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
// 构建的步骤 public Builder(String name) { this.name = name; } public Builder age(int val) { age = val; return this; } public Builder safeID(int val) { safeID = val; return this; } public Builder address(String val) { address = val; return this; } public DoDoContact build() { // 构建,返回一个新对象 return new DoDoContact(this); } } private DoDoContact(Builder b) { age = b.age; safeID = b.safeID; name = b.name; address = b.address; }} |
最终,客户程序可以很灵活的去构建这个对象。
|
1
2
3
4
|
DoDoContact ddc = new DoDoContact.Builder("Ace").age(10) .address("beijing").build();System.out.println("name=" + ddc.getName() + "age =" + ddc.getAge() + "address" + ddc.getAddress()); |
Builder模式好处和优点
使用Builder模式必然会导致写两遍相关属性的代码和SETTER方法,看起来有点吃力不讨好。然而需要看到的是,客户端代码的可用性和可读性得到了大大提高。与此同时,构造函数的参数数量明显减少调用起来非常直观。
Builder方法另外一个优势在于,单个builder构建多个对象时Builder参数可在创建期间进行调整,还可以根据对象不同而进行改变。这就像我越来越推崇的以“不变”应“万变”。Builder模式特别适合那些属性个数很多的类,我认为没有必要给那些本不需要设置值传递参数(设置null)。
Builder模式在提高代码可读性的同时,使用IDE提供的代码补全功能也更加容易。Builder模式在与构造函数一起使用带来的更大优势在Josh Bloch的Effective Java第二版第2条中有详细阐述。
Builder模式的代价和缺点
使用Builder模式是肯定会增加代码量的。此外,尽管客户端的代码可读性明显改善,但随之而来的客户端代码变得更加冗长。我还是坚持这一观点:相比较参数数量的增加,相同类型的参数混在一起,可选参数的增加而言,改善代码可读性更有价值。
Builder会增加个类代码,这也意味着开发者在给类增加属性时有时会忘记给该属性添加支持的builder。为了克服这个问题,通常我会将builder嵌套到类中,这样可以很容易地发现哪个相关的builder需要更新。尽管忘记的风险依旧存在,但是这风险就像忘记给类的新属性增加toString()、 equals(Object)、 hashCode()或其他类基于是所有属性的方法一样。
在我的Builder实现中,我会用Builder的构造函数而不是set方法传递客户需要的属性。这样做的好处在于,对象总是能被一次完整的实例化,而不是靠开发人员调用时用set方法补充额外的属性完成实例化。这也体现了不可变性带来的好处。然而,相应地也会造成自己设定的属性方法可读性降低。
正如它名字表示的那样,Builder只是一个替代构造器的选择不能直接用于降低非构造函数方法的参数数量,但是结合参数对象的使用就能达到这一点。有关更多反对用Builder来进行对象构建的讨论可以参见 A dive into the Builder pattern上的相关评论。
总结
构建对象时,如果碰到类有很多参数——其中很多参数类型相同而且很多参数可以为空时,我更喜欢Builder模式来完成。当参数数量不多、类型不同而且都是必须出现时,通过增加代码实现Builder往往无法体现它的优势。在这种情况下,理想的方法是调用传统的构造函数。再者,如果不需要保持不变,那么就使用无参构造函数调用相应的set方法吧。
Builder模式在Java中的应用的更多相关文章
- Builder模式在Java中的应用(转)
在设计模式中对Builder模式的定义是用于构建复杂对象的一种模式,所构建的对象往往需要多步初始化或赋值才能完成.那么,在实际的开发过程中,我们哪些地方适合用到Builder模式呢?其中使用Build ...
- Android Builder模式在开发中的应用
最近在学习图片加载框架Glide的时候,被他精简的写法震惊了.一句话,就可以搞定. Glide.with(mContext) .load(url) .centerCrop() .placeholder ...
- Active Object 并发模式在 Java 中的应用--转载
原文地址:http://www.ibm.com/developerworks/cn/java/j-lo-activeobject/ 本文主要从以下两个方面进行阐述: 使用 C++ 语言,来描述 Act ...
- 代理模式与java中的动态代理
前言 代理模式又分为静态代理与动态代理,其中动态代理是Java各大框架中运用的最为广泛的一种模式之一,下面就用简单的例子来说明静态代理与动态代理. 场景 李雷是一个唱片公司的大老板,很忙, ...
- 10分钟了解 代理模式与java中的动态代理
前言 代理模式又分为静态代理与动态代理,其中动态代理是Java各大框架中运用的最为广泛的一种模式之一,下面就用简单的例子来说明静态代理与动态代理. 场景 李雷是一个唱片公司的大老板,很忙, ...
- 浅谈代理模式与java中的动态代理
代理模式的定义: 代理模式是一个使用律非常高的模式,定义如下: 为其他对象提供一种代理,以控制对这个对象的访问. 类图: 简单的静态代理: public interface IRunner{ //这是 ...
- Java设计模式(3)建造者模式(Builder模式)
Builder模式定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. Builder模式是一步一步创建一个复杂的对象,它允许用户可以只通过指定复杂对象的类型和内容就可以构 ...
- Java Builder 模式,你搞懂了么?
加油.png 前言:最近闲来无事的时候想着看看一些平常用的三方库源码,没想到看了之后才知道直接撸源码好伤身体,一般设计优秀的开源库都会涉及很多的设计模式,就比如 android 开发使用频繁的 okH ...
- 同事写了一个疯狂的类构造器,我要疯了,Builder 模式都不会么?!
疯狂的类构造器 最近栈长在做 Code Review 时,发现一段创建对象的方法: Task task = new Task(112, "紧急任务", "处理一下这个任务 ...
随机推荐
- Html-浅谈如何正确给table加边框
一般来说,给表格加边框都会出现不同的问题,以下是给表格加边框后展现比较好的方式 <style> table,table tr th, table tr td { border:1px so ...
- 励志经典,持续收集ing....
士兵突击励志经典:http://blog.sina.com.cn/s/blog_660538e10100r7ld.html 励志散文1:http://www.vipyl.com/Article/328 ...
- OpenCV安装与配置
本文使用OpenCV2.48在win10平台下操作. 一,关于OpenCV OpenCV是开源C++计算机可视化处理库,它集成了很多计算机图形图像处理的功能.还有机器学习,字符识别,人脸识别,物体检测 ...
- css:子元素div 上下左右居中方法总结
最近在面试,不停地收到了知识冲击,尤其是对于一些基础的css.html.js问题居多,所以自我也在做反思,今天就css问题,如何让一个子元素div块元素上下左右居中 (以下总结方法,都已得到验证). ...
- 读取TDrawGrid之获取博易数据
朋友叫我帮忙写个从博易读取数据的工具,可无奈数据所在控件并不是Windows标准控件,也就是说没办法通过发送系统消息来获取 相关数据,于是乎试了一下从内存直接读取,可最后并不能达到预期目的,原因是笔者 ...
- 简单的maven配置
groupId是指com.xx 组织标识 artifactId才是项目名称 2)编译源代码 mvn compile 3)编译测试代码 mvn test-compile 4)清空 mvn clean 5 ...
- layer弹出框小结
1.layer.open() // 1.打开弹出层 layer.open({ type:1, //基本层类型 icon:, //图标 content:'请核对信息!', //内容 shade:0.3, ...
- OOP,WEB开发实用小技巧
偶然读到一篇博客,记录一下心得.这种设计对于新手来说一般是想不到的,它充分的发挥了OOP语言的特性,让代码专用而清爽.这是不是重构的思想呢? 我们在写业务层的时候,有很多方法是重复功能的,我们就可以使 ...
- Java之使用Hadoop探索大数据的世界
什么是大数据 PB = 1024tb 7123913827189tb Reids 无共享 HDFS 优点 :特别适合存储大型文件 TFS hdfs 架构 NameNode: 整个hadoop总管,只有 ...
- JS 循环给li绑定参数不同的点击事
以下内容纯属个人理解,不正确的地方还请大神留言,不胜感激! 源代码:(按个人方式选用一种即可) <ul> <li>1</li> <li>2</li ...