Java 《Effective Java 中文版 第2版》学习笔记 遇到多个构造器时要考虑用构建器

  静态工厂和构造器有个共同的局限性:它们都不能很好地扩展到大量的可选参数。

  当一个类中有若干个必选属性和多个可选属性时,采用重叠构造器模式、JavaBeans模式或者Builder模式,但各有优劣。

  当有很多参数的时候,重叠构造器模式下客户端代码会很难编写,并且仍然较难以阅读。

  JavaBeans模式调用一个无参构造器来创建对象,然后调用setter方法来设置每个必要的参数,以及每个相关的可选参数。因为构造过程被分到了几个调用中,在构造过程中JavaBean可能处于不一致的状态。与此相关的另一点不足在于,JavaBeans模式阻止了把类做成不可变的可能。

  Builder模式不直接生成想要的对象,而是让客户端利用所有必要的参数调用构造器(或者静态工厂),得到一个builder对象,然后客户端在builder对象上调用类似于setter的方法,来设置每个相关的可选参数,客户端最后调用无参的build方法来生成不可变的对象。这个builder是它构建的类的静态成员类。

  下面就是它的示例:

 1 public class NutritionFacts {
 2     private final int servingSize;
 3     private final int servings;
 4     private final int calories;
 5     private final int fat;
 6     private final int sodium;
 7     private final int carbohydrate;
 8
 9     public static class Builder {
10         // Required parameters
11         private final int servingSize;
12         private final int servings;
13
14         // Optional parameters - initialized to default values
15         private int calories = 0;
16         private int fat = 0;
17         private int carbohydrate = 0;
18         private int sodium = 0;
19
20         public Builder(int servingSize, int servings) {
21             this.servingSize = servingSize;
22             this.servings = servings;
23         }
24
25         public Builder calories(int val) {
26             calories = val;
27             return this;
28         }
29         public Builder fat(int val) {
30             fat = val;
31             return this;
32         }
33         public Builder carbohydrate(int val) {
34             carbohydrate = val;
35             return this;
36         }
37         public Builder sodium(int val) {
38             sodium = val;
39             return this;
40         }
41
42         public NutritionFacts build() {
43             return new NutritionFacts(this);
44         }
45     }
46
47     private NutritionFacts(Builder builder) {
48         servingSize = builder.servingSize;
49         servings = builder.servings;
50         calories = builder.calories;
51         fat = builder.fat;
52         sodium = builder.sodium;
53         carbohydrate = builder.carbohydrate;
54     }
55
56     public static void main(String[] args) {
57         NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).
58                 calories(100).sodium(35).carbohydrate(27).build();
59     }
60 }

  它在检验约束时将参数从builder拷贝到对象中之后,并在对象域而不是builder域中对它们进行检验。如果违反了任何约束条件,build方法就应该抛出IllegalArgumentException。对多个参数强加约束条件的另一个方法是,用多个setter方法对某个约束条件必须持有的所有参数进行检查。一旦传递了无效的参数,立即就会发现约束条件失败,而不是等着调用build方法。与构造器相比,builder的微略优势在于,builder可以有多个可变参数。构造器就像方法一样,只能有一个可变参数。

  Java中传统的抽象工厂实现是Class对象,newInstance方法总是企图调用类的无参构造器,这个构造器甚至可能根本不存在。Class.newInstance破坏了编译时的异常检查。Builder模式也存在不足。为了创建对象,必须先创建它的构建器。在十分注重性能的情况下,可能就成问题了。Builder模式还比重叠构造器模式更加冗长,因此它只在有很多参数的时候才使用,比如4个或者更多个参数。通常最好一开始就使用构建器。

  如果类的构造器或者静态工厂中具有多个参数,设计这种类时,Builder模式就是种不错的选择,特别是当大多数参数都是可选的时候。与使用传统的重叠构造器模式相比,使用Builder模式的客户端代码将更易于阅读和编写,构建器也比JavaBeans更加安全。

  参考资料

  《Effective Java 中文版 第2版》 P9-13

时间: 2024-10-31 05:05:04

Java 《Effective Java 中文版 第2版》学习笔记 遇到多个构造器时要考虑用构建器的相关文章

C++Primer第5版学习笔记(一)

C++Primer第5版学习笔记(一) 第一.二章的重难点内容 本篇文章主要记录了我在学习C++Primer(第5版,中文版)中遇到的重难点及其分析.因为第一.二章比较简单,因此这里合并这两章我遇到的问题.        第一章 开始 这一章在第一部分之前,是一个helloworld式的章节,包含基本的函数,io流以及类的介绍. 知识点1:P19,1.5,文件重定向 可以在windows下的cmd中或者mac,linux系统的终端窗口中用输入命令的形式执行程序并使它从一个文件中读入数据,再把标准

C++ Primer 第五版学习笔记

<C++ Primer>第五版中文版学习笔记 ? C++ Primer 第五版学习笔记

stm32寄存器版学习笔记07 ADC

STM32F103RCT有3个ADC,12位主逼近型模拟数字转换器,有18个通道,可测量16个外部和2个内部信号源.各通道的A/D转换可以单次.连续.扫描或间断模式执行. 1.通道选择 stm32把ADC转换分成2个通道组:规则通道组相当于正常运行的程序:注入通道组相当于中断.程序初始化阶段设置好不同的转换组,系统运行中不用变更循环转换的配置,从而达到任务互不干扰和快速切换. 有16个多路通道.可以把转换组织成两组:规则组和注入组.在任意多个通道上以任意顺序进行的一系列转换构成成组转换.例如,可

stm32寄存器版学习笔记05 PWM

STM32除TIM6和TIM7外都可以产生PWM输出.高级定时器TIM1和TIM8可以同时产生7路PWM,通用定时器可以产生4路PWM输出. 1.TIM1 CH1输出PWM配置步骤 ①开启TIM1时钟,配置PA8为复用输出 APB2外设时钟使能寄存器(RCC_APB2ENR) APB1外设复位寄存器 (RCC_APB1RSTR) 置1开启.清0关闭. Eg:RCC->APB2ENR|=1<<11; //使能TIM1时钟 配置I/O口: 参见stm32寄存器版学习笔记01 GPIO口的配置

C++Primer第5版学习笔记(三)

C++Primer第5版学习笔记(三) 第四/五章的重难点内容 你可以点击这里回顾第三章内容 因为第五章的内容比较少,因此和第四章的笔记内容合并.   第四章是和表达式有关的知识,表达式是C++的基础设施,本章由三部分组成:         1.表达式概念基础,包括表达式的基本概念,左值和右值的概念,优先级结合律,求值顺序.  2.各种运算符,主要包括算数\关系\逻辑\赋值\递增递减\成员访问\条件\位运算\sizeof\逗号运算符 这10种运算符.  3.类型转换,包括隐式和显式两种转换的规则

stm32寄存器版学习笔记06 输入捕获(ETR脉冲计数)

STM32外部脉冲ETR引脚:TIM1-->PA12;TIMER2-->PA0:TIMER3-->PD2;TIMER4-->PE0… 1.TIM2 PA0计数 配置步骤 ①开启TIM2时钟,配置PA0输入 APB1外设复位寄存器 (RCC_APB1RSTR) APB2外设时钟使能寄存器(RCC_APB2ENR) 置1开启.清0关闭. Eg:RCC->APB1ENR|=1<<0; //使能TIM2时钟  RCC->APB2ENR|=1<<2;  

C++ Primer(第五版)学习笔记_9_标准模板库_multimap多重映照容器

C++ Primer(第五版)学习笔记_9_标准模板库_multimap多重映照容器 多重映照容器multimap与map结构基本相同,但由于重复键值存在,所以multimap的元素插入.删除.查找都与map的方法不相同. 1.multimap对象创建.元素插入 插入元素时,需要使用insert()方法和类似pair<string,double>("Jack", 300.5)的元素结构.可以看到,重复的元素是按照插入的先后顺序排序的. #include <iostre

javascript权威指南第6版学习笔记

javascript权威指南第6版学习笔记 javascript数组.函数是特殊对象 看一点少一点. 3.1.4 hello.js内容是 var x=.3-.2;var y=.2-.1 console.log(x==y);console.log(x==.1);console.log(y==.1); 控制台输出结果: 3.1.5 var now = new Date();console.log(now); 3.2 文本 Javascript没有字符型,只有字符串. 16位怎么理解 3.10 变量作

C++ Primer(第五版)学习笔记_5_标准模板库string(2)

C++ Primer(第五版)学习笔记_5_标准模板库string(2) 10.搜索string对象的元素或子串 采用find()方法可查找字符串中的第一个字符元素(char, 用单引号界定)或者子串(用双引号界定):如果查到,则返回下标值(从0开始计数),如果查不到,则返回一个很大的数string:npos(即:4294967295). #include <iostream> #include <stdio.h> #include <string> using nam