02.当构造参数过多时使用builder模式
前言
《Effective Java》中文第三版,是一本关于Java基础的书,这本书不止一次有人推荐我看。其中包括我很喜欢的博客园博主五月的仓颉,他曾在自己的博文《给Java程序猿们推荐一些值得一看的好书》中也推荐过。加深自己的记忆,同时向优秀的人看齐,决定在看完每一章之后,都写一篇随笔。如果有写的不对的地方、表述的不清楚的地方、或者其他建议,希望您能够留言指正,谢谢。
《Effective Java》中文第三版在线阅读链接:https://github.com/sjsdfg/effective-java-3rd-chinese/tree/master/docs/notes
是什么
builder模式 是创建对象的一种方式。常见的对象创建方式还有 JavaBeans模式、可伸缩方法模式 等。
哪里用
扩展到很多可选参数的情景,“很多可选参数”指的是,当你的构造方法入参达到比如四个或更多时。
一个实体类,你预感它会在后期添加新的字段时,你也可以预先使用builder模式,因为你一开始没有使用,在后期类的参数演化到失控的时候,再想转为builder模式,之前写的代码就会是无用功。
怎么实现
我们通过 JavaBeans模式、可伸缩方法模式 与 Builder模式 创建对象,进行对比的方法,来看看Builder的实现,以及它的优点。
首先,我们来考虑一个场景,一瓶饮料,它的主要原料有很多,例如维生素、水、白砂糖等。我们先用可伸缩方法模式,来实现这样的一个类,代码如下:
public class Drink { //水 必填参数
private final int water; //糖 必填参数
private final int sweet; //维生素 可选参数
private final int vitamins; //饮料瓶颜色 可选参数
private final String color; public Drink(int water, int sweet){
this(water, sweet, 0);
} public Drink(int water, int sweet, int vitamins){
this(water, sweet, 0, "白色");
} public Drink(int water, int sweet, int vitamins, String color){
this.water = water;
this.sweet = sweet;
this.vitamins = vitamins;
this.color = color;
} public static void main(String[] args) {
Drink wowHaHa = new Drink(0, 0);
} }
当我们想要创建一个对象时,可以用构造方法入参最少的方法来创建(上方的例子最少传入两个参数:水、糖来创建一个Drink类)。通常情况下,可伸缩方法模式 的创建方法让你传入一些你不想设置的参数,但你不得不为它们传递一个值。上面的例子(只有)四个属性的入参,可能看起来不是很糟糕,如果有十个,二十个呢?那它很快就会失控。
简而言之,可伸缩构造方法模式当有很多参数时,很难编写客户端代码,而且很难读懂它。
为了解决'很多参数'这个问题(构造方法中遇到许多可选参数),我们另一种选择是使用 JavaBeans 模式,在这种模式中,调用一个无参的构造方法来创建对象,然后调用 setter
方法来设置每个必需的参数和可选参数,实例代码如下:
public class Drink02 {
//水 必填参数
private int water; //糖 必填参数
private int sweet; //维生素 可选参数
private int vitamins = -1; //饮料瓶颜色 可选参数
private String color = "白色"; public Drink02(){ } //Setters
public void setWater(int val) {water = val;}
public void setSweet(int val) {sweet = val;}
public void setVitamins(int val) {vitamins = val;}
public void setColor(String val) {color = val;} //使用 JavaBeans 模式创建一个对象
public static void main(String[] args) {
Drink02 waHaHa = new Drink02();
waHaHa.setWater(300);
waHaHa.setSweet(10);
waHaHa.setVitamins(1);
waHaHa.setColor("黑色瓶子");
}
}
JavaBeans 模式,没有 可伸缩方法模式 的缺点,但是代码看起来有点冗长,但创建实例很方便,并且生成的代码可读性更高。
但是,JavaBeans 模式是存在严重缺陷的,由于构造方法被分割成了多次调用,所以在设置属性中(构造过程中) JavaBean 可能处于不一致的状态。该类没有通过检查构造参数参数的有效性来强制一致性的选项。在不一致的状态下尝试使用对象可能会导致一些错误。例如一个需要设置一个必选参数,但是我们并没有设置。幸运的是,我们还有第三种选择:Builder 模式,代码实例如下:
public class Drink03 {
//水 必填参数
private final int water; //糖 必填参数
private final int sweet; //维生素 可选参数
private final int vitamins; //饮料瓶颜色 可选参数
private final String color; public static class Builder {
private final int water; private final int sweet; private int vitamins = 0; private String color = "白色"; public Builder(int water, int sweet){
this.water = water;
this.sweet = sweet;
} public Builder vitamins(int val){
vitamins = val;
return this;
} public Builder color(String val){
color = val;
return this;
} public Drink03 build(){
return new Drink03(this);
}
} private Drink03(Builder builder){
water = builder.water;
sweet = builder.sweet;
vitamins = builder.vitamins;
color = builder.color;
} //使用 Builder 模式创建一个对象
public static void main(String[] args) {
new Builder(100,10)
.vitamins(1).color("黑色").build();
}
}
因为所有默认参数都在一个地方,所以我们的Drink03对象是不可变的,setter赋值的操作,我们在builder方法中已经完成了,这样我们可以进行链式调用。这样的代码,在创建对象的时候非常容易编写,也易于阅读。
总结
Builder 模式创建对象
优点:
- 更加灵活。Builder灵活的选择了可选参数,也可以自动填充必选参数。
- 更加安全。
缺点:
- 为了创建对象,首先必须创建它的 builder。在看中性能的场合下这可能就是一个问题。而且,builder 模式比伸缩构造方法模式更冗长,因此有在有足够的参数时才值得使用它。
总而言之,当设计类的构造方法或静态工厂的参数超过几个时,Builder 模式是一个不错的选择,特别是如果许多参数是可选的或相同类型的。builder模式客户端代码比使用 伸缩构造方法 更容易读写,并且builder模式比 JavaBeans 更安全。
02.当构造参数过多时使用builder模式的更多相关文章
- Effective Java 第三版——2. 当构造方法参数过多时使用builder模式
Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...
- 当构造方法参数过多时使用builder模式
静态工厂和构造方法都有一个限制:它们不能很好地扩展到很多可选参数的情景.请考虑一个代表包装食品上的营养成分标签的例子.这些标签有几个必需的属性——每次建议的摄入量,每罐的份量和每份卡路里 ,以及超过 ...
- 构造器参数过多时考虑使用构建器(Builder)
一.静态工厂和构造器的局限性 面对需要大量可选参数才能构建对象时,静态工厂和构造器并不能随着可选参数的增加而合理扩展. 假设创建一个类Person需要使用大量的可选参数,其中两个参数是必填的,剩下的都 ...
- 【原】使用Builder模式替代构造参数传参
前言:关于传递参数,当参数过多的时候我们可以考虑使用建造者模式. #没用 Builder模式 之前是这样传参的: 如下所示,构造方法里面的参数一大堆,看起来就非常的混乱. 用了Builder模式之后是 ...
- 建造(Builder)模式
建造模式可以将一个产品的内部表象与产品的生成过程分割开来,从而可以使一个建造过程生成具有不同的内部表象的产品对象. 摘自EffectiveJava:当构造方法参数过多时使用建造者模式. 产品的内部表象 ...
- GOF对Builder模式的定义(转载)
(1)意图 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. (2)适用性 1. 当创建复杂对象的算法应该独立于该对象的组成部分以及他们的装配方式:2. 当构造过程必须允许构 ...
- 第二条 一个类如果有多个参数,考虑用Builder构造者模式
1. @Data public class Student { //体检用 private String name; private int age; private int height; priv ...
- 【Effective Java】2、构造参数过多的时候
package cn.xf.cp.ch02.item2; /** * * 功能:当我们的构造参数有很多,超出可控范围的时候,用build模式 时间:下午8:25:05 文件:NutritionFact ...
- 【代码优化】当许多构造函数的参数,请考虑使用builder模式
静态工厂和构造具有共同的局限性:我们不能扩展到大量的非常好的可选参数. 1.对于多个可选參数的构造器.我们都习惯採用重叠构造器模式.比方一个參数的构造器调用2个參数的构造器. 2个參数的构造器 ...
随机推荐
- drf三大组件之认证组件与权限组件
复习 """ 视图家族 1.视图类:APIView.GenericAPIView APIView:作为drf的基础view:as_view()禁用csrf:dispatc ...
- 前端知识之html
html介绍 web服务器的本质 import socket sk=socket.socket() sk.bind(('127.0.0.1'.8080)) sk.listen(5) while Tru ...
- Linux实现树莓派3B的国密SM9算法交叉编译——(一)环境部署、简单测试与eclipse工程项目测试
这篇文章主要介绍了交叉编译的实现,包括环境部署,并简单测试交叉编译环境是否安装成功. 一.交叉编译 在一个平台上生成另一个平台上的可执行代码.为什么要大费周折的进行交叉编译呢?一句话:不得已而为之.有 ...
- CGMH:Constrained Sentence Generation by Metropolis-Hastings Sampling解读
根据关键字生成句子: 读进关键字,随机选择处理手段(增删改)以及待处理word的位置,然后计算接受/拒绝概率,根据概率生成一个新的序列,再循环这一过程,循环次数是500,每次都将困惑度最低的生成句子放 ...
- C语言最重要的知识点(电子文档)
总体上必须清楚的: 1)程序结构是三种: 顺序结构 .选择结构(分支结构).循环结构. 2)读程序都要从main()入口, 然后从最上面顺序往下读(碰到循环做循环,碰到选择做选择),有且只有一个m ...
- 任意模数 n 次剩余
\(n\) 次剩余 你需要解方程 \(x^n\equiv k\pmod m\),其中 \(x\in [0,m-1]\). 保证解数不超过 \(C=10^6\) \(1\le n,m,k\le 10^9 ...
- python进阶(十七)正则&json(上)
1. 一个列表中所有的数字都是重复2次,但是有一个数字只重复了一次. 请找出重复一次的数字,不可以使用内置函数. [2,2,1,1,0,4,3,4,3] 方法1:通过字典计数,找到value等于1的k ...
- javascript中offsetWidth、clientWidth、width、scrollWidth、clientX、screenX、offsetX、pageX
原文:https://www.cnblogs.com/ifworld/p/7605954.html 元素宽高 offsetWidth //返回元素的宽度(包括元素宽度.内边距和边框,不包括外边距) o ...
- 关于 appium get_attribute 方法的坑
(得要学着看源码) 问题: self.driver.find_element_by_id("id").get_attribute("content-desc") ...
- Java web 会话技术 cookie与session
一.会话 会话可简单理解为:用户开一个浏览器,点击多个超链接,访问服务器多个web资源,然后关闭浏览器,整个过程称之为一个会话. 会话过程中要解决的一些问题 每个用户在使用浏览器与服务器进行会话的过程 ...