具有所有类属性的构造函数或带有setter的默认构造函数?

以下是两种方法:

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

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

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

  • setter和默认的空构造函数

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

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

你会做什么以及为什么? 你知道任何光模式(考虑到应该在每次实例化7个以上的属性时使用它)吗? 我问这个是因为我倾向于不喜欢大型构造函数,我无法快速找出我正在寻找的变量的位置,另一方面我发现“set all properties”容易遗漏一些属性。

请随意在优点和缺点中论证我的假设,因为它们只是我的想法:)

更新 – 我发现的一个与此相关的问题: 构建大的,不可变的对象,而不使用具有长参数列表的构造函数

您可以查看Joshua Bloch倡导的Builder模式,并在Effective Java中进行了描述。 http://developers.sun.com/learning/javaoneonline/2007/pdf/TS-2689.pdf上有一些主要内容的演示文稿; 毫无疑问,你可以挖掘出更好的参考。

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

例如,假设我有一个带有一些属性的简单Message 。 构造它的客户端代码可以使用构建器来准备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; } } 

你错过了拥有带有大量参数的构造函数的最大专家:它允许你创建不可变类型。

创建不可变类型而不会产生巨大构造函数的常规方法是使用辅助类型 – 一个构建器 ,它维护您在最终对象中需要的值,然后在准备好时构建不可变对象。

最近关于API可用性的学术研究(CMU和Microsoft)表明,具有setter的默认构造函数将成为可用性的方法。 这来自Jeff Stylos和Steven Clarke撰写的“对象构造函数中需要参数的可用性含义”,并在国际软件工程大会上发表:

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

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

getter和setter非常容易调试,特别是如果你提出了在没有正确填充对象的情况下抛出运行时exception的安全措施。 而且我是“易于调试”的忠实粉丝。

在此线程之前,我从来没有听说过Rob提到的Builder模式。 从来没有使用它(很明显),但这很有趣。

出于前面提到的不变性原因,我更喜欢使用构造函数参数。 如果这给你一个带有很多参数的构造函数(比如说超过四个),那对我来说就是代码味道:其中一些参数应该捆绑在一起成为它们自己的类型。

例如,如果你有这样的东西:

 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代码库中,太大的类是一个非常常见的设计问题。

还有其他方面。 如果你想在设计时而不是仅仅在运行时使用你的类来做某些事情,例如在Class Palette中添加你的类作为对象(这是使用Netbeans的Java),你需要提供一个无参数的构造函数为了能够这样做。

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

setter和默认的空构造函数

JRL倾向于触及它 ,但考虑使用setter的一个原因是让对象符合JavaBean规范 。 这使得实例可以通过内省工具进行编辑 ,并使用某些序列化技术进行持久化 。

谁说你做不到两者兼而有之? 我会说强制属性进入构造函数,可选属性用setter处理。 BTW,谁说你每个房产总需要一个二人? 如果两个属性在概念上属于一个,为什么不将它们组合在一起?

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