【发布时间】:2011-11-19 07:35:21
【问题描述】:
我一直在阅读干净的代码书,其中指出该类不应公开其数据的内部状态,而只应公开行为。如果一个非常简单和愚蠢的 java bean 暴露了 getter 和 setter 的内部状态,难道不值得只删除它们并将私有成员公开吗?还是只是将类视为数据结构?
【问题讨论】:
标签: java coding-style
我一直在阅读干净的代码书,其中指出该类不应公开其数据的内部状态,而只应公开行为。如果一个非常简单和愚蠢的 java bean 暴露了 getter 和 setter 的内部状态,难道不值得只删除它们并将私有成员公开吗?还是只是将类视为数据结构?
【问题讨论】:
标签: java coding-style
使成员可访问的问题是您不再从类内部控制它们。
假设您使 Car.speed 可访问。现在,在您的程序中的任何地方都可以引用它。现在,如果您想确保速度永远不会设置为负值(或者因为您需要使其线程安全而使更改同步),您必须:
在所有可以访问速度的点,重写程序以添加控件。并希望以后更改程序的每个人都记得这样做。
再次将成员设为私有,创建 getter 和 setter 方法,并重写程序以使用它们。
最好习惯从头开始编写getter和setter。现在,无论如何,大多数 IDE 都会自动为您完成。
【讨论】:
对此的规范答案是:您不知道您的简单数据结构将来是否会保持如此简单。它的发展可能比你现在预期的要多。也有可能,您很快想要在那个 bean 中有一些“值改变”的观察者。使用 getter 和 setter 方法,您可以在以后非常简单地执行此操作,而无需更改现有代码库。
getter/setter 的另一个优点是:如果在罗马,就像罗马人一样……这意味着在这种情况下:许多通用框架期望 getter/setter。如果您不想从一开始就排除所有这些有用的框架,那么请您和您的同事帮个忙,只需实现标准的 getter/和 setter。
【讨论】:
术语 POJO 旨在将类与 JavaBeans 或任何其他约定区分开来。因此,根据定义,POJO 不需要做任何事情。
我一直在阅读干净的代码书,其中指出该类不应公开其数据的内部状态,而只应公开行为。
这叫做封装,是一个很好的原则。
如果一个非常简单和愚蠢的 java bean 暴露了 getter 和 setter 的内部状态,难道不值得只删除它们并将私有成员公开吗?
这是另一种方法。一些项目可能会禁止这种方法,而另一些项目可能会鼓励它。就个人而言,我更喜欢这种方法用于已经以某种方式封装的类,例如它们是本地包。
有一种观点认为,有一天,您的班级可能会有额外的要求,而更改“API”将是不可能的。这违反了 YAGNI 原则,并且很少被证明是这种情况,而且当它确实比添加许多不做任何事情的方法时成本要低得多。
但是,情况并非总是如此,如果您不使用访问器方法,您应该考虑如果您以后必须更改它会对项目产生什么影响。在任何地方都使用访问器方法意味着您永远不必担心这一点。
总而言之,如果您非常确定访问器方法毫无意义并且以后添加它们不会有问题,我会说您应该使用您的判断。但是,如果您不确定将来是否会出现问题,或者您不想担心它,请使用访问器方法。
【讨论】:
我不这么认为。这取决于您的对象的生命周期及其“暴露”(外部修改)。
如果您仅将其用作数据结构,那么以安全方式(最终)公开字段就足够了:
public class Person {
public final String firstName;
public final String lastName;
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
【讨论】:
这就是@Peter Lawrey 对封装的解释。
只有一个注意事项:当您处理复杂对象时(例如在 ORM 项目的域模型中),当您拥有不是简单 Java 类型的属性时,这一点更为重要。例如:
public class Father {
private List childs = new ArrayList();
public Father() {
// ...
}
private List getChilds() {
return this.childs;
}
public void setChilds(List newChilds) {
this.childs = newChilds;
}
}
public class Child {
private String name;
// ...
private String getName() {
return this.name;
}
public void setName(String newName) {
this.name = newName;
}
}
如果您将一个属性(如Father 类中的childs 属性)公开,您将无法识别代码的哪一部分正在设置或更改您所公开属性的一个属性(例如,在Father 中添加新的Child,甚至更改现有Child 的name)。在示例中,只有Father 对象可以检索childs 内容,所有其余类都可以使用其设置器更改它。
【讨论】:
我认为使用 getter 和 setter 是个好主意,除非您有非常具体的速度/内存/效率要求或非常简单的对象。
一个很好的例子是Point,在这里公开它的.x 和.y 变量可能更好也更有效。
也就是说,如果您突然需要 setter 中的一些逻辑,那么即使对于大型代码库,更改一些成员变量的可见性并引入 getter 和 setter 实际上也不是什么大工作。
【讨论】:
JavaBean 需要 getter 和 setter。 POJO 没有,反正这有它的好处
getter 和 setter 的目的是实现封装,对对象的内部状态进行管理。这允许您在应用程序实施后在应用程序中添加或更改业务规则,仅更改 getter 或 setter 代码,例如,如果您有一个仅允许超过 3 个字符的文本字段,可以在将其分配给属性之前检查并抛出一个异常,不这样做的另一个原因是你是否可能想要更改实现或更改变量名称或类似的东西。如果该字段可公开访问且可修改,则无法强制执行此操作 无论如何,您都可以使用 IDE 生成 setter 和 getter。
如果你正在开发一个简单的应用程序可以推荐,如果你的应用程序很复杂并且必须给予维护不推荐。
【讨论】:
POJO 的定义不要求 getter/setter。
在实验上,我没有在当前项目中使用 getter 和 setter。
我采取的方法是这样的:
除非必要,否则不要提供 getter/setter。
到目前为止,我还没有找到真正需要 get/set 的案例。
一些朋友告诉我:“如果将来你需要 xyz,拥有 get/set 会很有帮助”;我的回答是:当 - 将来 - 我需要这样做时,我将提供 getter 和 setter;我不想预测任何事情。
有些人可能提出的关于封装的反对意见实际上并不是一个有效的反对意见:提供 getter 和 setter 以相同的方式破坏封装,而且您还有额外的(无用的)代码行。错误也可能存在于 getter 和 setter 中。
这是一个重要领域类之一的示例:
public class SSHKey implements IsSerializable {
public Long id;
public Long userId;
public String type;
public String bits;
public String fingerprint;
public String comment;
@SuppressWarnings("unused")
private SSHKey() { // required by gwt-rpc
}
public SSHKey(String text) throws InvalidSSHKeyException {
Ensure.that(text != null, new InvalidSSHKeyException("Invalid Key"));
text = text.trim();
String[] parts = text.split(" ", 3);
Ensure.that(parts.length >= 2,
new InvalidSSHKeyException("Invalid Key"));
type = getType(parts);
Ensure.that(type.equals("ssh-rsa") || type.equals("ssh-dss"),
new InvalidSSHKeyException(
"Key must start with 'ssh-rsa' or 'ssh-dss'"));
bits = getBits(parts);
comment = getComment(parts);
}
private String getBits(String[] parts) {
return parts[1];
}
private String getComment(String[] parts) {
if (parts.length == 3)
return parts[2];
return type + " " + bits.substring(0, min(15, bits.length())) + "...";
}
private String getType(String[] parts) {
return parts[0];
}
}
构造函数负责验证和准备可管理的数据。因此,此逻辑不需要在 setter/getter 中。
如果几年前我被公众成员反对,我可能不会喜欢他们;也许我现在做错了什么,但我正在试验,到目前为止还可以。
此外,您需要考虑您的类是否设计为可扩展的(因此,预见未来是要求的一部分),以及您是否希望您的对象是不可变的。那些你只能用 get/set 做的事情。
如果你的对象必须是不可变的,并且你可以避免空的构造函数,你可以在成员实例中添加'final',顺便说一句。 不幸的是,我不得不添加 IsSerializable(类似于 java.io.Serializable)和一个空的构造函数,因为这是 gwt 所必需的。所以,你可以告诉我“你看到了吗?你需要 getter 和 setter”;不太确定。
顺便说一句,有些 jdbc 框架提倡使用公共字段,例如 http://iciql.com 这并不意味着这个项目是正确的,而是有人在考虑它。
我认为 getter/setter 的需求主要是文化上的。
【讨论】:
仅当您在超出您控制范围的库中公开一个类时。
如果您确实发布了这样的库,Uniform Access Principle 规定您应该使用 getter 和 setter,以便以后能够更改底层实现,而无需客户端更改其代码。 Java 没有提供其他机制来执行此操作。
如果您在自己的系统中使用此类,则无需:您的 IDE 可以轻松封装公共字段并在一个安全步骤中更新其所有用法。在这种情况下,简洁会胜出,而在需要封装的时候不会有任何损失。
【讨论】:
对于数据类型对象,如 POJO / PODS / JavaBean,在 python 中你只有公共成员
您可以轻松设置并获取它们,而无需生成样板 setter 和 getter 代码(在 java 中,这些样板代码通常(98%)暴露内部私有标签,如问题中所述)
在 python 中,如果您需要与 getter 进行交互,那么您只需为此目的定义额外的代码
在语言层面上干净有效
在 java,他们选择了 IDE 开发而不是更改基础 java,参见 JavaBean,例如那有多大了,java 1.0.2 有多老了...
JDK 1.0(1996 年 1 月 23 日)
EJB 规范最初由 IBM 于 1997 年开发,后来在 1999 年被 Sun Microsystems(EJB 1.0 和 1.1)采用
所以只是忍受它,使用setter getter,因为它们是由java环境强制执行的
【讨论】: