Effective Java (2) 遇到多个构造器时要靠虑使用构建器

来源:互联网 发布:什么软件可以看美股 编辑:程序博客网 时间:2024/05/16 06:58

摘要:

用静态工厂方法创建对象,在遇到多参数构造器时,往往会变得方法集合臃肿。这是可以考虑使用以下3种方法改善:

一是使用层叠式构造器,二是使用无参构造器+Getter/Setter方法,三是使用构建器(Builder)

这3种方法的优劣如下所示:

方法优点缺点层叠式构造器代码简单,没有多线程问题的困扰依然无法彻底解决多参构造器,构造对象选择多样,导致方法集合臃肿的问题无参构造器+Getter/Setter代码相对简单需要考虑多线程问题,无法构造不可变属性使用构造器(Builder)客户端代码灵活易读,没有多线程问题需要额外的构造器代码针对不同场景,可以选择不同的方式创建多参对象。但是如果对象初始化参数很多,构建的组合也很多时,建议使用Builder,是代码清晰、易读且易扩展。


正文:

假设我们有一个类,名叫Diamond

public class Diamond {private Double caratage;private String color;private String clarity;private String cut;}

如果我们打算创建这样一个对象,一般会使用一个构造方法

public Diamond(Double caratage, String color, String clarity, String cut){  this.caratage = caratage;  this.color = color;  this.clarity=clarity;  this.cut=cut;}
但有时,我们需要通过不同的属性参数创建对象。于是,层叠式的构造器就成为了一种创建的方式:

public class CascadeConstructorDiamond {private Double caratage;private String color;private String clarity;private String cut;public CascadeConstructorDiamond(Double caratage) {this.caratage = caratage;}public CascadeConstructorDiamond(Double caratage, String color) {this.caratage = caratage;this.color = color;}public CascadeConstructorDiamond(Double caratage, String color, String clarity) {this.caratage = caratage;this.color = color;this.clarity = clarity;}public CascadeConstructorDiamond(Double caratage, String color, String clarity, String cut) {this.caratage = caratage;this.color = color;this.clarity = clarity;this.cut = cut;}}

但使用这样的方法会带来2个问题,一是创建的客户端代码不易读:

CascadeConstructorDiamond diamond1 = new CascadeConstructorDiamond(2d);CascadeConstructorDiamond diamond2 = new CascadeConstructorDiamond(2d, "A");CascadeConstructorDiamond diamond3 = new CascadeConstructorDiamond(2d, "A", "FL");CascadeConstructorDiamond diamond4 = new CascadeConstructorDiamond(2d, "A", "FL", "4EX");
虽然可以用静态工厂方法,改善这个问题。但另一个问题却无法改善,就是随着参数的增加,构造器数量渐渐失控。


于是一种可行的方法,就是使用JavaBean形式,既无参构造器+Getter/Setter方法

public class JavaBeanDiamond {private Double caratage;private String color;private String clarity;private String cut;public Double getCaratage() {return caratage;}public void setCaratage(Double caratage) {this.caratage = caratage;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}public String getClarity() {return clarity;}public void setClarity(String clarity) {this.clarity = clarity;}public String getCut() {return cut;}public void setCut(String cut) {this.cut = cut;}public static void main(String[] args) {JavaBeanDiamond diamond = new JavaBeanDiamond();diamond.setCaratage(2d);diamond.setColor("A");diamond.setClarity("FL");diamond.setCut("4EX");}}

使用JavaBean是一种简单的方法,客户端代码也相对清晰。但是它的缺点是,构造不是原子操作,需要考虑线程安全问题。另外,Setter方法是不支持给不可变属性(final)赋值的,所以这种方法无法很好支持不可变属性对象的初始化。


进而,使用构建器可以同时避免层叠式构造器和JavaBean模式的缺点:

public class BuilderDiamond {private Double caratage;private String color;private String clarity;private String cut;@Overridepublic String toString() {return String.format("caratage : %f, color : %s, clarity: %s, cut : %s", caratage, color, clarity, cut);}public Double getCaratage() {return caratage;}public String getColor() {return color;}public String getClarity() {return clarity;}public String getCut() {return cut;}private BuilderDiamond(DiamondBuilder builder) {this.caratage = builder.caratage;this.color = builder.color;this.clarity = builder.clarity;this.cut = builder.cut;}public static class DiamondBuilder {private Double caratage;private String color;private String clarity;private String cut;public DiamondBuilder caratage(Double caratage) {this.caratage = caratage;return this;}public DiamondBuilder color(String color) {this.color = color;return this;}public DiamondBuilder clarity(String clarity) {this.clarity = clarity;return this;}public DiamondBuilder cut(String cut) {this.cut = cut;return this;}public BuilderDiamond build() {return new BuilderDiamond(this);}}public static void main(String[] args) {BuilderDiamond diamond = new BuilderDiamond.DiamondBuilder().caratage(2d).clarity("FL").color("A").cut("4EX").build();System.out.println(diamond.toString());}}

通过一个内嵌的Builder,将属性赋值,并在最后调用Builder.build()方法时,返回要构造的最终对象。这样客户端代码既清晰,又不会带来线程问题。而且随着被构造类的参数增加,改造构造器代码可以使构造过程不至于在方法数量上失控。只是代价是,需要创建和维护一个构建类。(或许做一个代码生成工具可以使Builder的创建更加简介。)

综上所属,在不同场景下,可以选出不同的方式构造多参对象。但是如果对象参数很多,且存在组合,可以考虑使用构建器(Builder)使构造过程更清晰和易扩展。

0 0
原创粉丝点击