java中的builder模式

来自《effective+java中文版》:

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

重叠构造器模式

如果我们有一个类,表示一个用户的个人信息,有些值是必须的,有些不是必须的。

必须的参数:用户名、年龄、性别

非必须的参数:身高、住址、体重

public class User {

	private String username;
	
	private int age;
	
	private int sex;
	
	private float height;
	
	private String address;
	
	private float weight;
	
	public User(String username,int age,int sex){
		this(username,age,sex,0);
	}

	public User(String username,int age,int sex,float height){
		this(username,age,sex,height,"");
	}

	public User(String username,int age,int sex,float height,String address){
		this(username,age,sex,height,address,0f);
	}
	public User(String username,int age,int sex,float height,String address,float weight){
		this.username = username;
		this.age = age;
		this.sex = sex;
		this.height = height;
		this.address = address;
		this.weight = weight;
	}
}
当你想要创建实例的时候,利用参数列表最短的构造器,该列表包含了要设置的参数:

User u = new User("china", 23, 1, 55.5f, "",179.2f);
这个构造器调用通常许多你不想设置的参数,但还是不得不为他们传值。随着属性列表的增多,很快就会失去控制。

重叠构造器模式可行,但是当有许多参数的时候,客户端代码会很难写,并且难以阅读

如果读者想知道某一个参数是什么意思,还需要仔细的数着参数个数来叹究竟。可能会导致微妙的错误,很可能一不小心两个参数位置颠倒了,微妙的错误也可能影响极大的后患。

遇到许多构造器参数的时候,还有一种javabbeans模式,这种模式只需要一个午餐的构造方法,然后通过setter方法给每个必要的参数和不必要的参数进行赋值:

public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public int getSex() {
		return sex;
	}

	public void setSex(int sex) {
		this.sex = sex;
	}

	public float getHeight() {
		return height;
	}

	public void setHeight(float height) {
		this.height = height;
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	public float getWeight() {
		return weight;
	}

	public void setWeight(float weight) {
		this.weight = weight;
	}
	

这种模式弥补了重叠构造器模式的不足,创建实例容易,代码也易读。

但是,javabeans模式本身有着很严重的缺点,因为构造过程被分到几个调用中,构造过程中可能出现javabean不一致的状态,类无法仅仅通过构造器参数的有效性来保证一致性,试图使用处于不一致的状态,将会导致失败。javabean模式阻止了把类做成不可变的可能。程序员需要通过额外的努力来保证线程的安全。

幸运的是,还有第三种方法,既能保证像重叠器模式那样的安全性,也能保证javabeans模式那样的可读性,这就是Builder模式的一种形式。不直接生成想要的对象,而是让客户端利用所有必要的参数调用构造器,得到一个builder对象,然后客户端在builder对象上调用类似setter的方法,来设置每个相关的可选参数。最后客户端调用无参的builder方法生成不可变的对象。实例:

public class User {

	private String username;

	private int age;

	private int sex;

	private float height;

	private String address;

	private float weight;

	public User(Builder builder) {
		username = builder.username;
		age = builder.age;
		sex = builder.sex;
		height = builder.height;
		address = builder.address;
		weight = builder.weight;
	}

	public static class Builder {
		// required
		private String username = "";
		private int age = 0;
		private int sex = 1;
		// optional
		private float height = 0.0f;
		private String address = "";
		private float weight = 0.0f;

		public Builder(String username, int age, int sex) {
			this.username = username;
			this.age = age;
			this.sex = sex;
		}

		public Builder height(float val) {
			height = val;
			return this;
		}

		public Builder address(String val) {
			address = val;
			return this;
		}

		public Builder weight(float val) {
			weight = val;
			return this;
		}

		public User build() {
			return new User(this);
		}
	}

	public String getUsername() {
		return username;
	}

	public int getAge() {
		return age;
	}

	public int getSex() {
		return sex;
	}

	public float getHeight() {
		return height;
	}

	public String getAddress() {
		return address;
	}

	public float getWeight() {
		return weight;
	}

}

调用代码:

public class TestBuilder {

	public static void main(String[] args) {
		User user = new User.Builder("china",23,1).address("hangzhou").build();
		System.out.println(user.getUsername()+","+user.getAge()+","+user.getSex()+","+user.getAddress());
	}
}

打印出:

china,23,1,hangzhou。

同样,还可以设置更多参数:

User user = new User.Builder("china",23,1).address("hangzhou").height(1.78f).build();

user.getHeight()可以获取到:1.78

这样客户端代码更容易编写,更易于阅读。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值