Java:为什么要使用方法而不是构造函数?

4
假设你有一个名为 Person 的类,而一个 Person 有诸如 nameidage 等属性。与其在构造函数中设置这些值,我们可以采用以下方法:
new Person().withName("Lorem").withId("1234").withAge(29)

with方法是调用set方法并返回对象的方法,例如:

public Person withAge(int age) {
    this.setAge(age);
    return this;
}

在一个当前的项目中,我看到了很多这样的代码,通常有5-10个链接到不同with方法的链式调用。与在构造函数中设置这些值相比,这样做有什么好处呢?


1
代码更易读:new Person("a", "b", "c") 不容易表明 a、b 和 c 是什么。 - JB Nizet
1
顺便说一下,Java不允许在单引号内使用字符串 :) - ajb
1
@ajb,他们发明了Kotlin :-) - assylias
1
如果您返回 this,则应该是 public Person withAge - msagala25
2
@ZhekaKozlov 提示很有帮助,但这并不意味着可以不以更易读的方式编写代码。在我的工作中,我们许多人使用 IntelliJ,但也有人使用 Eclipse,并且我们还有查看其他团队代码的浏览器方式,而不必将其下载到 IDE 中。因此,编写难以理解的代码是不够好的,不能指望每个人都使用 IntelliJ 来理解它。这是行不通的。 - ajb
显示剩余9条评论
4个回答

3

与在构造函数中设置这些值相比,这样做有哪些好处?

1)重载

如果需要设置许多参数,但其中一些是可选的,则可以轻松管理要设置的值的数量,而无需创建特定构造函数或传递null值。

 new Person("name", 19);
 new Person("name", 19, address);

 new Person("name", 19, phone);

在您的情况下,您只需要调用所需的方法(与设置器相同)。

2) 标识

此外,方法/构造函数中有很多参数往往难以阅读,难以识别每个参数的上下文。

 new Person("frank", "John", "Emma");
 person.withName("frank").withFather("john").withMother("Emma");

将参数传递给方法/构造函数时,参数是没有名称的,您需要检查签名以了解您正在传递什么。通过这种记号,您可以编写更冗长和易读的代码(同样适用于setter)。 3) 可链式设置器 与此处不同,使用设置器时将执行相同操作但不具有可链式特征。
person.setName("name");
person.setAge(19);

person.withName("name").withAge(19);

除了可读性之外,我认为没有真正的改进,这个链需要一个方法来返回实例本身,这在类本身中会产生冗余代码(return this;)。

1
我同意,我从来没有理解为什么这种方法链接如此受到炒作。 - Henry

2

我看到的一个优点是易读性。

如果我们扩展这个例子

new Person()
.withName("Lorem")
.withId("1234")
.withAge(29)
.withHeight(170)
.withWeight(75)
.withTaxId("1234");

如果我们不使用这种模式,而是使用构造函数模式,那么当我们使用参数时,会出现许多没有任何描述的参数。

new Person("Lorem","1234",29,170,75,"1234");

2
这段话的意思是:“这一切都关乎可读性和流畅的接口。虽然你的例子不是很适合流畅接口,但你可以在维基百科文章的Java部分找到一个例子。”
Author author = AUTHOR.as("author");
create.selectFrom(author)
      .where(exists(selectOne()
                   .from(BOOK)
                   .where(BOOK.STATUS.eq(BOOK_STATUS.SOLD_OUT))
                   .and(BOOK.AUTHOR_ID.eq(author.ID))));

流畅的接口设计是 API 设计中非常高级的话题,通常需要使用某种构建器模式以避免过多和过大的构造函数)或外观模式(以避免难以理解的、主要为内部使用的 API)。
一个相当优雅的流畅 API 通常需要深刻理解您的需求,并进行非常好的规划/准备阶段。
请注意,使用流畅的API与编写DSL密切相关。这里有一个由Martin Fowler提供的示例,以及对流畅接口的扩展、非常好的解释,包括其优点、缺点、理论等等。

1
这就是了,我知道我在某个地方看过那个符号! - AxelH
@AxelH 是的,我添加了一些细节 - 没想到答案仍然会引起很多关注,所以一开始我没有花太多精力。 - Do Re

1

似乎有两个主要优点:

1.) 灵活性:使用这种模式,您可以基本上选择要填充和不填充的字段。而使用构造函数,则需要多个构造函数才能实现相同的功能。如在 Person 中。

new Person().withName("Loren")

或者它可以是

new Person().withName("Loren").withAge(30)

其它所有项目都是 null/默认值。

如果使用构造函数初始化,你需要为这两个都编写 2 个构造函数。

public Person(String name){
  //code
}

public Person(String name, String age){
//code
}

2.) 如其他答案所提到的,可读性。
new Person().withName("Loren").withAge(30).withId(567)

更易读。

new Person("Loren", 30, 567)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接