如果构造函数参数超过4个,可以考虑使用builder创建对象。
如下,MultiConstructorShoe提供了多个构造函数,以避免使用包含最多参数的构造函数:
package com.bingo.practice.effective.two.two;
public class MultiConstructorShoe {
//鞋的名称
private String name;
//鞋的大小
private int size;
//鞋的颜色
private int color;
//鞋的类型,如男鞋、女鞋,童鞋
private int sex;
public MultiConstructorShoe(String name,int size){
this(name,size,0,0);
}
public MultiConstructorShoe(String name,int size,int color){
this(name, size,color,0);
}
public MultiConstructorShoe(String name,int size,int color,int sex){
this.name=name;
this.size=size;
this.color=color;
this.sex=sex;
}
public String getName() {
return name;
}
public int getSize() {
return size;
}
public int getColor() {
return color;
}
public int getSex() {
return sex;
}
}
可以使用JavaBean形式,使用setter分别设置属性值。此方式的缺点在于无法保证对象被使用前,所有必需的属性值都已设置。比如保证name,size属性都已被设置后才使用此对象
package com.bingo.practice.effective.two.two;
public class JavaBeanShoe {
//鞋的名称
private String name;
//鞋的大小
private int size;
//鞋的颜色
private int color;
//鞋的类型,如男鞋、女鞋,童鞋
private int sex;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public int getColor() {
return color;
}
public void setColor(int color) {
this.color = color;
}
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
}
以下是使用Builder创建对象,此方法的优点在于:1.避免使用包含过多参数的构造函数 2.保证所有必需属性设置完成后才返回所创建的对象 3.可使用链式语法设置属性
package com.bingo.practice.effective.two.two;
public class BuilderShoe {
//鞋的名称
private String name;
//鞋的大小
private int size;
//鞋的颜色
private int color;
//鞋的类型,如男鞋、女鞋,童鞋
private int sex;
public String getName() {
return name;
}
public int getSize() {
return size;
}
public int getColor() {
return color;
}
public int getSex() {
return sex;
}
public static class Builder{
private String name;
private int size;
private int color;
private int sex;
public Builder(String name,int size){
this.name=name;
this.size=size;
}
public Builder name(String name){
//可添加校验逻辑
this.name=name;
return this;
}
public Builder size(int size){
this.size=size;
return this;
}
public Builder color(int color){
this.color=color;
return this;
}
public Builder sex(int sex){
this.sex=sex;
return this;
}
public BuilderShoe build(){
//可添加校验逻辑
BuilderShoe shoe=new BuilderShoe();
shoe.name=this.name;
shoe.size=this.size;
shoe.color=this.color;
shoe.sex=this.sex;
return shoe;
}
}
}
以上3个类测试代码如下,使用builder方式的确更优美:
package com.bingo.practice.effective.two.two;
import org.testng.Assert;
import org.testng.annotations.Test;
public class BuilderTest {
@Test
public void testMultiConstructorShoe(){
MultiConstructorShoe shoe=new MultiConstructorShoe("bingo", 42,0,0);
Assert.assertEquals(shoe.getName(), "bingo");
}
@Test
public void testJavaBeanShoe(){
JavaBeanShoe shoe=new JavaBeanShoe();
shoe.setName("bingo");
shoe.setSize(42);
shoe.setColor(0);
shoe.setSex(0);
Assert.assertEquals(shoe.getName(), "bingo");
}
@Test
public void testBuilderShoe(){
BuilderShoe shoe=new BuilderShoe.Builder("bingo", 42).color(0).sex(0).build();
Assert.assertEquals(shoe.getName(), "bingo");
}
}
builder可以配合工厂设计模式一起使用(原书指配合抽象工厂设计模式使用)。假设我们需要生产多种不同类型的鞋,每种鞋的制作工艺都不同。因此我们可以声明一个Builder接口,其每个实现类负责生产一种鞋。接口声明如下:
public interface Builder<T extends Shoe> {
T build();
}
接着声明工厂接口,如下。其中ShoeFactory的createShoe(builder)方法会调用传入的builder生产鞋:
public interface ShoeFactory<T extends Shoe> {
T createShoe(Builder<T> builder);
}
假设每种鞋都需要使用相同的Logo,并且需要设置生产日期,那么可以在工厂实现类里进行这些操作,如下:
public class BaseShoeFactory<T extends Shoe> implements ShoeFactory<T>{
public T createShoe(Builder<T> builder) {
T shoe=builder.build();
shoe.setLogo("MyShoeLogo");
shoe.setCreationDate(new Date());
return null;
}
}