Java链式构造器(译)

我们写代码时,随时都要创建对象。 通常我们使用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,而且还意味着保持所有代码内部的可维护性和可读性。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,993评论 19 139
  • 这是16年5月份编辑的一份比较杂乱适合自己观看的学习记录文档,今天18年5月份再次想写文章,发现简书还为我保存起的...
    Jenaral阅读 2,860评论 2 9
  • 深夜24点,看资料看不进去,白天茶水灌太多睡不着,加之下午受到感情挫伤,又来这里闲言碎语。 好久都没有出去走走看看...
    Ami小米阅读 311评论 1 0
  • 有一丝烦躁,明明什么都想依靠自己,却什么都不能自已; 有一丝烦躁,大家都在为未来奋斗拼搏时,我却看不清自己的未来;...
    瑜儿么么哒阅读 236评论 0 0
  • 关于作者 泰·田代是美国马里兰大学情感、性格以及成瘾症研究中心的资深研究员,探索传播公司旗下美体健康频道的情感专家...
    西门胖子阅读 573评论 0 0