具有所有类属性的构造函数或具有设置器的默认构造函数?
-
06-07-2019 - |
题
以下是两种方法:
- 具有所有类属性的构造函数
优点:我必须输入确切数量的参数类型,因此如果出错,编译器会警告我(顺便问一下,有没有办法防止在参数列表上错误地切换两个整数的问题?)
缺点:如果我有很多属性,实例化行可能会变得非常长,并且可能跨越两条或更多行
- setter 和默认的空构造函数
优点:我可以清楚地看到我正在设置的内容,因此如果我做错了什么,我可以在输入时立即查明它(我不能犯以前切换相同类型的两个变量的错误)
缺点:具有大量属性的对象的实例化可能需要几行(不知道这是否真的是一个骗局),如果我忘记设置一个属性,编译器不会说什么。
你会做什么以及为什么?您是否知道任何光模式(考虑到每次实例化具有 7 个以上属性的对象时都应该使用它)来建议?我问这个问题是因为我倾向于不喜欢大型构造函数,因为我无法快速找出我正在寻找的变量在哪里,另一方面我发现“设置所有属性”很容易丢失某些属性。
请随意争论我的假设的优点和缺点,因为它们只是我的想法:)
更新 - 我发现与此相关的一个问题: 构建大的、不可变的对象,而不使用具有长参数列表的构造函数
解决方案
您可以查看 Joshua Bloch 倡导的 Builder 模式,并在 有效的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;
}
}
其他提示
您已经错过了带有大量参数的构造函数的最大优点:它允许您创建不可变类型。
创建不可变类型的正常方法 没有 巨大的构造函数的肮脏之处在于有一个辅助类型 - a 建设者 它维护您在最终对象中所需的值,然后在准备好时构建不可变对象。
最近关于 API 可用性的学术研究(CMU 和 Microsoft)表明,就可用性而言,带有 setter 的默认构造函数将是最佳选择。这是来自 Jeff Stylos 和 Steven Clarke 的“对象构造函数中要求参数的可用性含义”,并在国际软件工程会议上提出:
抽象的:API 的可用性对于程序员的生产力越来越重要。根据特定 API 可用性研究的经验,探索了研究许多 API 通用设计选择的可用性的技术。进行了一项比较研究,以评估专业程序员如何在对象构造函数中使用带有所需参数的 API,而不是无参数的“默认”构造函数。据推测,必需的参数将通过指导程序员正确使用对象并防止错误来创建更多可用和自记录的 API。然而,研究发现,与预期相反,程序员强烈喜欢使用不需要构造函数参数的 API,并且使用起来更有效。使用认知维度框架分析参与者的行为,并揭示所需的构造函数参数会干扰常见的学习策略,导致不良的过早承诺。
您在帖子中提到了这一点,但我认为这是值得更多关注的重要一点:除非每个输入参数都是不同的类型,否则巨大的构造函数的大问题是它 非常 很容易转置几个变量。编译器是一个不可靠的安全网——它会捕获一些错误,但那些漏掉的错误将更难以识别和调试。特别是因为一个巨大的构造函数的输入列表是相当不透明的,除非您在另一个窗口中打开了 API。
getter 和 setter 更容易调试,特别是如果您制定了在对象未正确填充时抛出运行时异常的保护措施。我非常喜欢“易于调试”。
在这篇文章之前,我从未听说过 Rob 提到的构建器模式。我自己从未使用过它(显然),但它非常有趣。
出于上述不变性原因,我更喜欢采用构造函数参数。如果这给了你一个带有大量参数(比如超过四个左右)的构造函数,那么这对我来说就是一种代码味道:其中一些参数应该捆绑在一起成为它们自己的类型。
例如,如果你有这样的东西:
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 代码库中一个非常常见的设计问题。
还有其他方面。如果您希望能够在设计时而不是仅在运行时对您的类进行某些操作,例如将您的类作为对象添加到对象选项板中(这是使用 Netbeans 的 Java),您需要在为了能够这样做。
这里还有其他策略。在尝试弄清楚如何处理大量参数之前,我认为重新审视您的设计并查看您的类是否做得太多很重要。看看是否可以将一些参数组合到一个新类中,并将一些行为移到该类中。
setter 和默认的空构造函数
JRL斜着碰了一下, ,但考虑使用 setter 的原因之一是让对象符合 JavaBean规范. 。这使得实例可以 编辑 通过内省工具和坚持使用某些 序列化技术.
谁说你不能两者兼得?我想说强制属性进入构造函数,可选属性由设置器处理。顺便说一句,谁说每个房产都需要一名二传手?如果两个属性在概念上属于同一类,为什么不将它们设置在一起呢?
我也喜欢 Builder 模式,但最重要的规则是:始终开动你的大脑,找到最适合特定问题的设计。不存在一刀切的解决方案。