Java 具有所有类属性的构造函数还是具有setter的默认构造函数?

Java 具有所有类属性的构造函数还是具有setter的默认构造函数?,java,constructor,setter,Java,Constructor,Setter,以下是两种方法: 具有所有类属性的构造函数 优点:我必须输入精确数量的参数类型,因此如果我出错,编译器会警告我(顺便问一下,有没有办法防止错误切换参数列表上的两个整数?) 缺点:如果我有很多属性,实例化行可能会变得很长,并且可能跨越两行或更多行 setter和默认的空构造函数 优点:我可以清楚地看到我正在设置什么,所以如果我做错了什么,我可以在键入时立即指出它(我不能犯切换相同类型的两个变量的错误) 缺点:一个具有大量属性的对象的实例化可能需要几行代码(不知道这是否真的是一个缺点),如果

以下是两种方法:

  • 具有所有类属性的构造函数
优点:我必须输入精确数量的参数类型,因此如果我出错,编译器会警告我(顺便问一下,有没有办法防止错误切换参数列表上的两个整数?)

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

  • setter和默认的空构造函数
优点:我可以清楚地看到我正在设置什么,所以如果我做错了什么,我可以在键入时立即指出它(我不能犯切换相同类型的两个变量的错误)

缺点:一个具有大量属性的对象的实例化可能需要几行代码(不知道这是否真的是一个缺点),如果我忘记设置属性,编译器不会说什么

你会做什么?为什么? 您知道任何灯光模式(考虑到每次实例化具有7+属性的对象时都应该使用它)来建议吗? 我这样问是因为我不喜欢大型构造函数,因为我无法快速找出我要查找的变量的位置,另一方面,我发现“set all properties”容易丢失一些属性

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


更新-我发现了一个与此相关的问题:

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

创建不可变类型而不产生巨大构造函数缺陷的正常方法是使用助手类型—一个维护最终对象中所需值的生成器,然后在准备就绪时构建不可变对象。

最近的学术研究(CMU和Microsoft)关于API可用性的研究表明,就可用性而言,使用setter的默认构造函数将是一条可行之路。 这来自Jeff Stylos和Steven Clarke的“在对象的构造函数中要求参数的可用性影响”,并在软件工程国际会议上介绍:

:
API的可用性对于程序员的生产力越来越重要。基于对特定API可用性研究的经验,探索了研究许多API所共有的设计选择可用性的技术。进行了一项比较研究,以评估专业程序员如何在对象的构造函数中使用具有所需参数的API,而不是无参数的“默认”构造函数。据推测,所需的参数将通过指导程序员正确使用对象和防止错误来创建更可用和自文档化的API。然而,在研究中发现,与预期相反,程序员强烈倾向于使用不需要构造函数参数的API,并且更有效。使用认知维度框架分析参与者的行为,发现所需的构造器参数会干扰常见的学习策略,导致不必要的过早承诺。

您可以看看Joshua Bloch倡导的构造器模式,并在有效Java中进行了描述。这里有一个关于主要观点的演讲;毫无疑问,你可以找到更好的推荐人

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

例如,假设我有一条简单的
消息
,其中包含一些属性。构建它的客户机代码可以使用生成器准备
消息
,如下所示:

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;
    }

}

还有其他方面。如果您希望能够在设计时而不仅仅是在运行时使用类完成某些事情,例如在对象调色板中将类作为对象添加(这是使用Netbeans的Java),您需要提供一个无参数构造函数才能做到这一点。

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

getter和setter更容易调试,特别是如果您建立了保护机制,在对象未正确填充时引发运行时异常。我是“易于调试”的超级粉丝


在此之前,我从未听说过Rob提到的构建器模式。我自己从来没有用过它(显然),但它非常有趣。

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

setter和默认的空构造函数


,但是考虑使用定位器的一个原因是使对象符合。这使得实例可以通过内省工具和使用特定的持久性进行修改。

出于上述不变性原因,我更喜欢使用构造函数参数。如果这给了你一个包含很多参数(比如超过四个)的构造函数,那对我来说就是一种代码味道:这些参数中的一些应该捆绑到它们的
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) { ... }
}