2.2 使用builder替代多参数构造函数
如果构造函数参数超过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 {@Testpublic void testMultiConstructorShoe(){MultiConstructorShoe shoe=new MultiConstructorShoe("bingo", 42,0,0);Assert.assertEquals(shoe.getName(), "bingo");}@Testpublic void testJavaBeanShoe(){JavaBeanShoe shoe=new JavaBeanShoe();shoe.setName("bingo");shoe.setSize(42);shoe.setColor(0);shoe.setSex(0);Assert.assertEquals(shoe.getName(), "bingo");}@Testpublic 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;}}
?
?
?
?
?
?
?
?
?
?
?
?
?
?