`

【Java小技巧】避免过多的构造器参数

    博客分类:
  • Java
阅读更多

我们假设用户类有数十个属性,比如:姓名,性别,年龄等等。

 

如果使用单一的构造器,会造成构造器参数过多的问题。过多的构造器参数不但降低了代码的可读性,而且大大增加了程序员出错的几率。

 

比较普遍的解决方案有两个:重叠构造器和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

 

分享到:
评论

相关推荐

    editplus 代码编辑器html c++ jsp css

    很小但是功能却很强大,编辑网页可以随时预览,能够多人工作。 附使用手册: Editplus使用技巧 技巧中,在编译器集成例子中参照了部分官方的文献。有几篇是从网上搜集来的,这里我注明了来源或原始作者。如果你是...

    EditPlus 2整理信箱的工具

    (技巧提示:空行仅包括空格符、制表符、回车符,且必须以这三个符号之一作为一行的开头,并且以回车符结尾,查找空行的关键是构造代表空行的正则表达式)。 直接在"查找"中输入正则表达式“^[ \t]*\n”,注意\t前有...

    Editplus 3[1].0

    (技巧提示:空行仅包括空格符、制表符、回车符,且必须以这三个符号之一作为一行的开头,并且以回车符结尾,查找空行的关键是构造代表空行的正则表达式)。 直接在"查找"中输入正则表达式“^[ \t]*\n”,注意\t前有...

    mysql官方中文参考手册

    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. 设计...

    MYSQL中文手册

    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. 优化数据库结构 ...

    MySQL 5.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....

    MySQL 5.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. 设计...

    MySQL5.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. 设计...

    MySQL 5.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. 设计...

    MySQL 5.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. 设计...

    mysql5.1中文手册

    如何避免表扫描 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. 优化数据库...

    华为编程开发规范与案例

    在新的编程思想中,指针基本上被禁止使用(JAVA中就是这样),至少也是被限制使用。而在我们交换机的程序中大量使用指针,并且有增无减。 2、防止指针/数组操作越界 【案例1.2.1】 在香港项目测试中,发现ISDN话机...

    MySQL 5.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. 设计...

Global site tag (gtag.js) - Google Analytics