Java Beans: 过分夸大的关联数组?

7

我对Java Beans的本质并不是很了解,至少我在我们公司处理的一些代码库中看到的使用方式不是很清楚。

我找到了这个问题:

Java Beans: What am I missing?

那里被接受的答案让程序员滥用Java Bean(我真的不怀疑),但我经常看到它被故意滥用,我认为我仍然缺少某些东西。

我看到的代码看起来像:

public class FooBean {
  private int a;
  private int b;
  private int c;

  public int getA() { return a; }
  public int setA(int x) { a = x; }

  // etc...
}

没有比getter和setter更多的结构或控制。是否有某种超级棒的编译器技巧涉及反射、getter和setter,以及需要一些非常尴尬(但编译器优化)的静态关联数组?
或者我完全错过了重点。 :\
干杯!
编辑:
这里绝对不提倡公共字段的想法。

3
很遗憾,对于这个问题的大部分回答都完全错过了实际问题,并将其解释为“为什么需要getter和setter方法”... - Mike Daniels
从你们的说法来看,Java Beans 更像是一种蒙昧的编程实践,而不是其他什么。 - Jeremy Powell
@Mike:如果你认为你已经正确理解了,那就给出你的答案。 - dfa
只是想提一下——许多bean用于数据传输,没有任何数据。我得出的结论是这是代码的可怕浪费。我唯一能看到的原因是这些不仅仅是哈希表的类型安全性。现在需要像这样的属性包时,我实际上只是将属性的哈希封装到我的包中。我很想看到Java采用某种预定义的类型安全属性包模式,具有自动通知和验证,但大多数人似乎很高兴花费他们的时间编写setter和getter。 - Bill K
6个回答

8

实际上,是的,有一个神奇的过程正在进行。

这是一个非常愚蠢的模式,但GUI bean(所有组件都是)旨在通过GUI构建器进行反射分析。公共设置/获取字段旨在成为用户在构建GUI时可以操纵的“属性”。

编辑:为了保护Sun的利益,我应该说,虽然由于所有人都复制并开始在其非bean代码中使用它而导致该模式变得相当讨厌,但它确实允许人们在不使用任何外部元数据的情况下将类与GUI构建器集成。您不需要任何XML文件或属性文件来编辑器,只需扩展Button并将新对象放入面板即可。

这种模式已在其他地方使用,并且正如您所注意到的那样,它几乎与公共字段相同。我认为这是Sun在设置Java时创建的最糟糕的模式之一,但没有其他明显的解决方案(我认为整个概念在Java发布之前由试图构建第一个GUI构建器的第一批人员添加)。

现在有一个更好的解决方案:使用注释标记私有字段,反射工具仍然可以分析它们并将它们绑定到构建器控件。这将更加清洁,不会使您的所有对象如此容易受到外部状态更改的影响。


3

正如你所指出的,Java Beans 是一种相当琐碎的编码规范。

有用的部分在于它们本身就是一种规范。这使得可以构建基于辅助工具(当时)新颖想法的工具,例如精确指定 getter 和 setter 的名称,并期望特定方法存在而不需要归属于接口。这样可以提供足够的灵活性来实现任何想要的功能,但仍然允许您使用 GUI 来组装 Beans。

目前,随着完全内省成为常态,其他语言已经形式化了获取/设置功能,并出现了许多做 Beans 所能做的事情的其他方法,我认为它们是一个时代已经过去的想法。


对于会议的总结,如果你能做得好,我会给你一个加分。但是我不同意你的说法,即这个约定已经不再需要了。Spring就是一个例子,它广泛使用bean样式的装配。如果你有一个由Java组件大量组成的大型Web服务,通过XML文件将它们连接起来比编写Java代码进行连接更加整洁。 - Stephen C
1
现在大多数人会使用注解而不是bean样式的装配,因为用一个词来形容bean样式就是“糟糕”。 - Bill K

2

毫无疑问,人们确实过度使用了傻瓜式的getter/setter。

然而,试想一下两年后当你意识到你必须从你的setEmail(string email);方法中去除字符串中的空格并且所有的代码都硬编码以使用公共字段而不是方法调用时,你将会经历多少痛苦。

请记住面向对象设计的主要规则之一:“针对接口编程,而不是针对实现编程”。访问类的公共字段肯定是后者,这将使重构代码变得更加困难。


反对的不一定是getters和setters的概念,而是我们必须手写这些东西。 - skaffman
@skaffman,现代IDE提供模板,因此很容易自动生成代码。Eclipse可以为多个字段生成构造函数和get/set方法。 - Thorbjørn Ravn Andersen
1
好吧,说得也有道理,但你最终仍然会有大量的无用杂物。 - skaffman
@skaffman: IDE可以很容易地编写来隐藏“无用”的getter和setter,就像Eclipse可以隐藏'import'语句一样。但我没有注意到有一个这样做的IDE。要么是我没有很努力地寻找...要么是大多数程序员不关心要求IDE实现这一点。 - Stephen C
@nos,你的回答恰好展示了使用setter和getter的问题。你不应该向对象请求数据,而是要求它对其数据进行操作。因此,你可能有一个名称,但最好将其设置为final,并仅在构造函数中设置(如果可能的话,在设置字段之前可以轻松操纵它)...你可能不应该有一个getter,但它们不太严重...但你可能有一个getPreferredEmail(),它会返回存储的电子邮件地址之一,这不是一个纯getter。更好的方法是在你的对象中添加一个mailTo()方法。 - Bill K

1

你没有错过重点。Getter/Setter 看起来很丑陋,但是它们被嵌入到许多工具中,这些工具对类/方法/字段名称(例如 Spring)做出了一些假设,因此(除了它们明显的封装价值之外),它们已经成为几乎必需的要求,除非你在一个非常有限和私人的上下文中工作。


1
现在的Spring不是更喜欢注解吗?因为Beans很糟糕吗?顺便说一下,Setter和Getter方法是封装的反面,它们允许访问应该完全隐藏的内部状态。 - Bill K

0

如果没有setter,你就无法检查不变量:

public void setFoo(Foo foo) { 
     if (foo == null) {
          throw new InvalidFoo();
     }

     this.foo = foo;
}

使用公共成员无法做到这一点。其次,在setter中,您可以触发其他bean可以侦听的事件。最后但并非最不重要的是,您可以强制执行正确的类型检查(正如您在问题中指出的那样)。


这是一个公正的答案... 被反驳了! - skaffman
2
同意,与问题无关。像“水是湿的”这样的回答值得点赞吗?虽然它完全正确并且非常有用,但实际上并不适用于当前的问题。 - Bill K
1
此外,这是不符合不变量的反模式,最好将其设置为final并在构造函数中设置,这样您就可以确定甚至您自己的代码也无法设置它。如果您绝对需要使用生成器模式,则这将非常有用,但如果是这种情况,您应该返回“this”。 - Bill K
反模式?你不能将所有对象建模为不可变的。 - dfa
@Bill:不过你说得对,我的答案并不如被接受的那个好。 - dfa

0
“Getter”和“Setter”非常有用,如果您想要重写对数据的访问,例如用于日志/安全或返回与原始类不同的值。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接