我们假设用户类有数十个属性,比如:姓名,性别,年龄等等。
如果使用单一的构造器,会造成构造器参数过多的问题。过多的构造器参数不但降低了代码的可读性,而且大大增加了程序员出错的几率。
比较普遍的解决方案有两个:重叠构造器和Java Bean。我们将介绍并分析这两种方式的优劣并在最后给出一种更合理的解决方案。
重叠构造器
重叠构造器的构建首先需要分析所有属性。把属性非为必填和可选属性两类。第一类构造器提供所有的必填属性。第二类构造器的参数除了必填属性外增加一个可选属性,第三类增加两个可选属性,以此类推,直到构造器包含所有的属性。
比如说,我们的用户类只有姓名和性别是必填属性,那么我们可以如下构建重叠构造器(只罗列若干属性):
package com.joshua.code.sample; public class User { private String name; private int sex; private String nation; private String city; ... public User(String name, int sex) { this(name, sex, "unknown", "unknown"); } public User(String name, int sex, String nation) { this(name, sex, nation, "unknown"); } public User(String name, String city, int sex) { this(name, sex, "unknown", city); } public User(String name, int sex, String nation, String city) { this.name = name; this.sex = sex; this.nation = nation; this.city = city; } }
这个方案能屏蔽一些用户不关心的属性,在一定程度上缓解参数多的问题。细心的读者可能会发现,第二和第三个构造器的参数类型完全相同,在这种情况下,我们只能更换参数的顺序来避免构造器签名的重复,这是重叠构造器的第一大缺陷。此外,代码可读性的问题也没有根本性的解决。
Java Bean
Java bean的实现主要是私有化类的成员变量,并通过getter和setter来访问成员变量。相信大家都会写,IDE也可以自动生成,我就不上代码了。
它的优势在于构造器可以无参,使用setter时,代码的可读性好:
user.setNation("China");
设置属性值的过程一目了然。
缺点在于,构造对象的过程(包括为成员变量赋值)被分到了若干个方法中。这会导致构造过程的Java Bean处于不一致的状态。Java运行时环境不能保证对象在完全构建完成之前(含赋值)不被调用。并且程序员必须花精力来确保这个过程的线程安全性。
Builder
有什么方式可以兼顾重叠构造器的一致性和Java Bean的可读性呢?答案是Builder。我们先来看下用户编程接口:
User user = new User.Builder("Joshua", 0).age(10).city("Shanghai").build();
显而易见,上方的构建方式不但解决了代码可读性的问题并大幅减少了构造器的参数且构建过程保证了一定的一致性。这种具名的参数构建方式酷似Phython等语言。下面我们给出源码:
package com.joshua.code.sample.effective.builder; public class User { private String name; private int age; private int sex; private String city; private String nation; private long birthday; public static class Builder { // required private final String name; private final int sex; // optional private int age = 0; private String city = "unknown"; private String nation = "unknown"; private long birthday = 0L; public Builder(String name, int sex) { this.name = name; this.sex = sex; } public Builder age(int age) { this.age = age; return this; } public Builder city(String city) { this.city = city; return this; } public Builder nation(String nation) { this.nation = nation; return this; } public Builder birthday(long birthday) { this.birthday = birthday; return this; } public User build() { return new User(this); } } private User(Builder build) { this.age = build.age; this.birthday = build.birthday; this.city = build.city; this.nation = build.nation; this.name = build.name; this.sex = build.sex; } public String getName() { return name; } public int getAge() { return age; } public int getSex() { return sex; } public String getCity() { return city; } public String getNation() { return nation; } public long getBirthday() { return birthday; } public static void main(String[] args) { User user = new User.Builder("Joshua", 0).age(10).city("Shanghai").build(); } }
好的实现方式自然是人见人爱,Builder这种构建模式已经被Apache commons的lang项目吸收。你可以在该jar包内发现一个名为org.apache.commons.lang3.builder.Builder的接口:
public interface Builder<T> { public T build(); }
当然具体的实现还是要靠大家自己来做。
参考文档:
1. 《Effective Java》
2. Apache commons-lang API
相关推荐
Builder模式是一种创建型设计模式,它的目的是为了应对构造器参数过多而设计。Builder模式包含以下几部分: 1. 抽象建造者:通常是一个接口,包含建造方法和返回产品的方法。 2. 具体建造者:实现了抽象建造者接口...
在Java编程中,内存管理是优化程序性能和避免内存泄漏的关键环节。本教程将深入探讨Java语言中内存管理的几个重要技巧,旨在帮助开发者更好地理解和掌握这一核心概念。 1. **对象创建与垃圾回收** - **对象创建**...
6. 排版整洁:保持简洁、清晰,避免过多花哨设计。 7. 工具/网站推荐:利用LinkedIn、GitHub等平台提升个人影响力。 三、面试前关注的问题 1. 学校背景:学历不是决定因素,关键在于个人能力。 2. 非计算机专业:...
6. **避免过多的字段访问**:直接访问字段通常比反射快得多。如果需要频繁访问某个字段,考虑使用getter/setter方法或提供公共访问器,而非反射。 7. **谨慎使用AccessibleObject#setAccessible(true)**:这个方法...
- **避免使用Tab键**:由于不同编辑器对Tab键的处理方式不同,为了代码的一致性和可读性,应使用空格代替Tab键。 **详细解析:** - 在实际开发中,由于不同的IDE或编辑器对Tab键的设置可能存在差异,这可能导致在...
- 使用`Constructor`和`Method`对象调用构造器和方法。 - 设置`Accessible`属性来访问私有成员。 #### 十、设计模式 设计模式是一套被反复使用的解决方案模板,用于解决软件设计中的常见问题。 - **常用的设计...
初始化阶段负责执行类构造器`<clinit>`方法。 #### 2. 字节码执行引擎 JVM的核心组件之一就是字节码解释器,它负责执行字节码指令。此外,JVM还包含一个即时编译器(JIT Compiler),能够将热点代码编译成本地机器...
很小但是功能却很强大,编辑网页可以随时预览,能够多人工作。 附使用手册: Editplus使用技巧 技巧中,在编译器集成例子中参照了部分官方的文献。有几篇是从网上搜集来的,这里我注明了来源或原始作者。如果你是...
- **线程池管理**:使用线程池并发处理多个请求,避免创建过多线程消耗资源。 - **异步执行**:利用`HttpAsyncClient`进行异步请求,提高系统吞吐量。 7. 结论: HttpClient 4.5.2作为强大的HTTP客户端工具,为...
静态关键字用于修饰成员变量、成员方法和构造器。静态成员属于类而不是对象,这意味着它们可以在不创建对象的情况下直接通过类名访问。 ##### 7. final关键字的作用 final关键字用于声明常量、不可变类和方法。...
6. **使用工厂方法或构造器模式**:改善对象的创建过程,使其更加灵活和易于测试。 7. **引入参数对象**:当一个方法接收过多参数时,可以创建一个新的对象来封装这些参数。 8. **使用策略或访问者模式**:使代码...
2. 时间管理:合理分配时间,先易后难,避免在难题上耗费过多时间。 3. 团队协作:分工明确,充分利用每个队员的优势。 4. 实战演练:多参加模拟赛,积累实战经验,提升应对压力的能力。 以上是ACM比赛实务中关于...
(技巧提示:空行仅包括空格符、制表符、回车符,且必须以这三个符号之一作为一行的开头,并且以回车符结尾,查找空行的关键是构造代表空行的正则表达式)。 直接在"查找"中输入正则表达式“^[ \t]*\n”,注意\t前有...
(技巧提示:空行仅包括空格符、制表符、回车符,且必须以这三个符号之一作为一行的开头,并且以回车符结尾,查找空行的关键是构造代表空行的正则表达式)。 直接在"查找"中输入正则表达式“^[ \t]*\n”,注意\t前有...
7.2.15. 如何避免表扫描 7.2.16. INSERT语句的速度 7.2.17. UPDATE语句的速度 7.2.18. DELETE语句的速度 7.2.19. 其它优化技巧 7.3. 锁定事宜 7.3.1. 锁定方法 7.3.2. 表锁定事宜 7.4. 优化数据库结构 7.4.1. 设计...
7.2.15. 如何避免表扫描 7.2.16. INSERT语句的速度 7.2.17. UPDATE语句的速度 7.2.18. DELETE语句的速度 7.2.19. 其它优化技巧 7.3. 锁定事宜 7.3.1. 锁定方法 7.3.2. 表锁定事宜 7.4. 优化数据库结构 ...
7.2.15. 如何避免表扫描 7.2.16. INSERT语句的速度 7.2.17. UPDATE语句的速度 7.2.18. DELETE语句的速度 7.2.19. 其它优化技巧 7.3. 锁定事宜 7.3.1. 锁定方法 7.3.2. 表锁定事宜 7.4. 优化数据库结构 7.4.1....
7.2.15. 如何避免表扫描 7.2.16. INSERT语句的速度 7.2.17. UPDATE语句的速度 7.2.18. DELETE语句的速度 7.2.19. 其它优化技巧 7.3. 锁定事宜 7.3.1. 锁定方法 7.3.2. 表锁定事宜 7.4. 优化数据库结构 7.4.1. 设计...