24

以下是两种方法:

  • 具有所有类属性的构造函数

优点:我必须输入准确数量的参数类型,所以如果我出错,编译器会警告我(顺便说一句,有没有办法防止在参数列表上错误地切换两个 Integer 的问题?)

缺点:如果我有很多属性,则实例化行可能会变得很长,并且可能跨越两行或多行

  • setter 和默认的空构造函数

优点:我可以清楚地看到我在设置什么,所以如果我做错了什么,我可以在输入时立即查明它(我不能犯切换相同类型的两个变量的先前错误)

缺点:具有许多属性的对象的实例化可能需要几行(不知道这是否真的是一个缺点),如果我忘记设置一个属性,编译器不会说任何东西。

你会做什么,为什么?您是否知道建议的任何灯光模式(考虑到每次实例化具有 7 个以上属性的对象时都应该使用它)?我问这个是因为我倾向于不喜欢大型构造函数,因为我无法快速找出我正在寻找的变量在哪里,另一方面,我发现“设置所有属性”容易丢失一些属性。

随意争论我的利弊假设,因为它们只是我的想法:)

更新 - 我发现了一个与此相关的问题:Building big, immutable objects without using constructors have long parameter lists

4

9 回答 9

26

您已经错过了拥有带有大量参数的构造函数的最大优点:它允许您创建不可变类型。

创建不可变类型而没有大量构造函数讨厌的正常方法是拥有一个辅助类型 - 一个构建器,它维护您在最终对象中想要的值,然后在您准备好时构建不可变对象。

于 2009-05-06T17:06:05.347 回答
20

您可以查看 Joshua Bloch 倡导的 Builder 模式,并在Effective Java中进行了描述。在http://developers.sun.com/learning/javaoneonline/2007/pdf/TS-2689.pdf上有一个介绍要点;毫无疑问,您可以找到更好的参考。

基本上,您有另一个类,可能是一个内部类,它提供以设置的属性命名的方法,并返回原始构建器,以便您可以链接调用。它产生了相当可读的代码块。

例如,假设我有一个简单Message的几个属性。构建它的客户端代码可以使用构建器来准备 a Message,如下所示:

Message message = new Message.Builder()
    .sender( new User( ... ) )
    .recipient( new User( ... ) )
    .subject( "Hello, world!" )
    .text( messageText )
    .build();

的片段Message.Builder可能类似于以下内容:

public class Builder {

    private User sender = null;
    // Other properties

    public Builder sender( User sender ) {
        this.sender = sender;
        return this;
    }
    // Methods for other properties

    public Message build() {
        Message message = new Message();
        message.setSender( sender );
        // Set the other properties
        return message;
    }

}
于 2009-05-06T17:09:55.600 回答
6

最近关于 API 可用性的学术研究(CMU 和 Microsoft)表明,使用 setter 的默认构造函数将是可用性方面的最佳选择。这是来自 Jeff Stylos 和 Steven Clarke 的“对象构造函数中需要参数的可用性影响”,并在国际软件工程会议上发表:

抽象的:API 的可用性对程序员的生产力越来越重要。基于对特定 API 的可用性研究的经验,探索了用于研究许多 API 共有的设计选择的可用性的技术。进行了一项比较研究,以评估专业程序员如何在对象的构造函数中使用带有所需参数的 API,而不是无参数的“默认”构造函数。据推测,通过引导程序员正确使用对象并防止错误,所需的参数将创建更多可用和自记录的 API。然而,在研究中发现,与预期相反,程序员强烈倾向于使用不需要构造函数参数的 API 并且更有效。参与者

于 2009-05-06T17:06:51.663 回答
3

您在帖子中提到了它,但我认为这是值得更多关注的重点:除非每个输入参数都是不同的类型,否则大型构造函数的最大问题是转置几个变量非常容易。编译器是一个不可靠的安全网——它会捕捉到一些错误,但漏掉的错误将更加难以识别和调试。特别是因为一个巨大的构造函数的输入列表是非常不透明的,除非你在另一个窗口中打开了 API。

Getter 和 setter 更容易调试,特别是如果您制定了在对象未正确填充时引发运行时异常的安全措施。而且我是“易于调试”的忠实拥护者。

在此线程之前,我从未听说过 Rob 提到的 Builder 模式。我自己从未使用过它(显然),但它非常有趣。

于 2009-05-06T17:27:48.337 回答
2

出于上述不变性的原因,我更喜欢使用构造函数参数。如果这为您提供了一个带有大量参数(比如说四个以上)的构造函数,那对我来说就是一种代码味道:其中一些参数应该捆绑在一起成为它们自己的类型。

例如,如果你有这样的事情:

class Contact
{
    public Contact(string firstName, string lastName, string phoneNumber,
        string street, string city, string state, int zipCode) { ... }
}

我将其重构为:

class Contact
{
    public Contact(Person person, PhoneNumber number, Address address) { ... }
}

class Person
{
    public Person(string firstName, string lastName) { ... }
}

class PhoneNumber
{
    public PhoneNumber(string digits) { ... }
}

class Address
{
    public Address(string street, string city, string state, int zipCode) { ... }
}

过大的类是 OOP 代码库中一个非常常见的设计问题。

于 2009-05-06T21:53:22.237 回答
0

还有其他方面。如果您希望能够在设计时而不是在运行时对类进行某些操作,例如将您的类作为对象添加到 Object Palette(这是使用 Netbeans 的 Java)中,您需要在为了能够这样做。

于 2009-05-06T17:11:07.207 回答
0

这里还有其他策略。在试图弄清楚如何处理大量参数之前,我认为重新审视你的设计并看看你的类是否做得太多是很重要的。看看您是否可以将一些参数组合到一个新类中,并将一些行为移到该类中。

于 2009-05-06T17:50:37.797 回答
0

setter 和默认的空构造函数

JRL 间接地谈到了它,但考虑使用 setter 的一个原因是让对象符合JavaBean 规范。这使得实例可以通过自省工具和使用某些序列化技术的持久性进行编辑

于 2009-05-06T18:47:19.627 回答
0

谁说你不能两者兼得?我想说强制属性进入构造函数,可选属性由设置器处理。顺便说一句,谁说每个属性都需要一个二传手?如果两个属性在概念上属于一起,为什么不将它们设置在一起呢?

我也喜欢 Builder 模式,但最重要的规则是:始终使用你的大脑并找到最适合特定问题的设计。没有一种万能的解决方案。

于 2009-05-06T22:01:05.823 回答