我们写代码时,随时都要创建对象。 通常我们使用new关键字创建对象,这就涉及调用构造函数来初始化我们的对象。
假设我们有一个描述耳机的类,当中有两个属性:颜色和价格,默认的情况下,耳机是黑色的,售价150美元,于是我们有了以下代码
public class Headphones {
private final Color color;
private final BigDecimal priceInDollars;
public Headphones() {
this.color = Color.BLACK;
this.priceInDollars = BigDecimal.valueOf(150.0);
}
//other methods
}
一段时间后,我们决定制作不同颜色的耳机了,但是价格维持不变,对于这种情况,我们增加了一个新的构造函数:价格设置为默认值,但颜色通过参数传入构造方法:
public Headphones(final Color color) {
this.color = color;
this.priceInDollars = BigDecimal.valueOf(150.0);
}
再过一段时间后,我们决定不同颜色的耳机可以有不同的价格。 于是我们再次添加了一个新的构造函数:
public class Headphones {
private final Color color;
private final BigDecimal priceInDollars;
public Headphones() {
this.color = Color.BLACK;
this.priceInDollars = BigDecimal.valueOf(150.0);
}
public Headphones(final Color color) {
this.color = color;
this.priceInDollars = BigDecimal.valueOf(150.0);
}
public Headphones(final Color color, final BigDecimal priceInDollars) {
this.color = color;
this.priceInDollars = priceInDollars;
}
//other methods
}
我们可以看到,以上的实现导致编码重复。 每个构造函数都必须自己设置所有字段,如果我们想要更改默认值,我们必须改变所有构造函数。 那我们该怎么做呢?
想到的第一个解决方案是我们可以使用默认值:
public class Headphones {
private Color color = Color.BLACK;
private BigDecimal priceInDollars = BigDecimal.valueOf(150.0);
public Headphones() {
}
public Headphones(final Color color) {
this.color = color;
}
public Headphones(final Color color, final BigDecimal priceInDollars) {
this.color = color;
this.priceInDollars = priceInDollars;
}
//other methods
}
现在,我们的构造函数只设置他们想要更改的字段。
我们也可以用另一种方式完成它并使用初始化程序设置默认值:
public class Headphones {
private Color color;
private BigDecimal priceInDollars;
{
this.color = Color.BLACK;
this.priceInDollars = BigDecimal.valueOf(150.0);
}
//other
}
当然,这三种方式可以混合使用。 你可以通过构造函数设置一个字段,通过默认值设置第二个字段,并在初始化程序中设置第三个字段,但有什么好处呢?
我没有看到任何好处,反而发现很多问题:
-
我们没有一个统一的实现, 每个构造方法的实现都完全独立。
-
不够简洁,由于有多个实现,使用者需要关心每一个实现
-
After transformation, our fields cannot be final. We are setting default values regardless of whether we change them or not, so we have to make the possibility of changing it
-
Unnecessary field initialization is creating an unused object. This is not really a problem until you are creating millions of objects
我们怎么解决这些问题呢?
答案是使用链式构造器。
链式构造器意味着我们以一种方式编写构造函数,构造函数调用其他构造函数,最终到达统一的构造函数。 这个统一的构造函数称为主构造函数,所有其他构造函数称为辅助构造函数。
主构造函数将是我们创建和初始化对象的单一点,因此我们可以将所有常见行为放入其中。 为了保持一致性,我们只有一个主要的构造函数; 理想情况下,此构造函数将设置所有类字段。 此外,重要的是,如果我们不想对外公开主构造函数,那么主构造函数可以设置成私有的。
使用链式构造器,你不必担心代码重复。 因此,它将使你的代码更简单,更易于维护。 所有构造函数最终都调用主构造函数,这是一个可以共享公共代码的单点创建。
我建议你将主构造函数编写为类中的最后一个构造函数,并将其作为所有辅助函数编写,就像我在下面的Headphones所做的那样:
public class Headphones {
private final Color color;
private final BigDecimal priceInDollars;
public Headphones() {
this(Color.BLACK);
}
public Headphones(final Color color) {
this(color, BigDecimal.valueOf(150.0));
}
public Headphones(final Color color, final BigDecimal priceInDollars) {
this.color = color;
this.priceInDollars = priceInDollars;
}
//other methods
}
尽管Java提供了不同的方式实现初始化,我的建议是
- 永远不要使用初始化程序块,永远不要使用默认值初始化成员,始终链接构造函数。
在创建类时,请始终考虑如何正确设计它。 公开许多构造函数将提供不同的创建方式,从而增加代码的可用性。
设计类不仅意味着提供一个易单漂亮的API,而且还意味着保持所有代码内部的可维护性和可读性。
网友评论