以下是两种方法:
优点:我必须输入一定数量的参数类型,所以如果我发出错误,编译器会发出警告(顺便说一下,有没有办法防止错误地在参数列表上切换两个Integer的问题? )
缺点:如果我有很多属性,那么实例化行可以变得非常长并且可以跨越两行或更多行
优点:我可以清楚地看到我正在设置的内容,所以如果我做错了什么我可以在我输入时立即查明它(我无法做出切换两个变量的先前错误相同类型)
缺点:具有大量属性的对象的实例化可能需要多行(不知道这是否真的是骗局),如果我忘记设置属性,编译器就不会说任何内容。
你会做什么以及为什么? 你知道任何光模式(考虑到应该在每次实例化7个以上的属性时使用它)吗? 我问这个是因为我倾向于不喜欢大型构造函数,我无法快速找出我正在寻找的变量的位置,另一方面我发现“set all properties”容易遗漏一些属性。
随意在我们的利弊中论证我的假设,因为他们只是我的想法:)
更新 - 我发现的一个与此相关的问题:Building big, immutable objects without using constructors having long parameter lists
答案 0 :(得分:26)
你错过了拥有带有大量参数的构造函数的最大专家:它允许你创建不可变类型。
创建不可变类型而没有巨大的构造函数肮脏的常规方法是拥有一个帮助器类型 - 一个构建器,它维护你在最终对象中需要的值,然后在准备好时构建不可变对象。
答案 1 :(得分:20)
您可以查看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;
}
}
答案 2 :(得分:6)
最近关于API可用性的学术研究(CMU和Microsoft)表明,具有setter的默认构造函数将是可用性的方法。 这来自Jeff Stylos和Steven Clarke撰写的“对象构造函数中需要参数的可用性含义”,并在国际软件工程会议上发表:
Abstract: API的可用性对程序员的生产力越来越重要。基于对特定API的可用性研究的经验,探索了用于研究许多API共有的设计选择的可用性的技术。进行了一项比较研究,以评估专业程序员如何在对象的构造函数中使用具有必需参数的API,而不是无参数的“默认”构造函数。假设通过引导程序员正确使用对象并防止错误,所需参数将创建更多可用和自我文档化的API。然而,在该研究中,发现与预期相反,程序员强烈倾向于使用不需要构造函数参数的API更有效。使用认知维度框架分析参与者的行为,并揭示所需的构造函数参数会干扰常见的学习策略,从而导致不良的过早承诺。
答案 3 :(得分:3)
你在帖子中提到它,但我认为这是一个值得关注的重点:除非每个输入参数都是不同的类型,否则巨大的构造函数的大问题是非常容易转置几个变量。编译器是一个不可靠的安全网 - 它会捕获一些错误,但是那些漏掉的错误将更加难以识别和调试。特别是因为巨大的构造函数的输入列表是非常不透明的,除非你在另一个窗口中打开了API。
Getter和setter非常容易调试,特别是如果你提出了在没有正确填充对象的情况下抛出运行时异常的安全措施。而且我是“易于调试”的忠实粉丝。
在此线程之前,我从未听说过Rob提到的Builder模式。从来没有使用它(很明显),但这很有趣。
答案 4 :(得分: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代码库中,太大的类是一个非常常见的设计问题。
答案 5 :(得分:0)
还有其他方面。如果你想在设计时而不是仅仅在运行时使用你的类来处理某些事情,例如在Object Palette中添加你的类作为对象(这是使用Netbeans的Java),你需要提供一个无参数的构造函数为了能够这样做。
答案 6 :(得分:0)
此处还有其他策略。在试图弄清楚如何处理大量参数之前,我认为重新访问您的设计并查看您的课程是否做得太多非常重要。看看你是否可以将一些参数组合成一个新类,并将一些行为移到该类中。
答案 7 :(得分:0)
setter和默认的空构造函数
JRL obliquely touched on it,但考虑使用setter的一个原因是让对象符合JavaBean specification。这使得实例可以通过内省工具和使用某些editing的持久性来适应serialization techniques。
答案 8 :(得分:0)
谁说你不能同时做到这两点?我会说强制属性进入构造函数,可选的属性用setter处理。 BTW,谁说你每个房产总需要一个二人?如果两个属性在概念上属于一个,为什么不将它们组合在一起?
我也喜欢Builder模式,但最重要的规则是:始终使用你的大脑并找到最适合特定问题的设计。没有一个通用的解决方案。