Java Beans:我缺少了什么?

18

我在想,是不是我对Java Beans有所遗漏。我喜欢让我的对象在构造函数中尽可能地完成初始化,并尽量减少可变性。Beans似乎直接违反了这一点,并且通常感觉很笨重。如果我不将对象构建为Beans,我会错过哪些功能?

5个回答

14

看起来你走在了正确的道路上。不是你没有理解 Java Beans 的重点,而是其他程序员误用了它们。

Java Beans 规范是为了与可视化工具一起使用而设计的。其想法是应用程序设计人员可以交互式地配置一个对象实例,然后将配置的 Bean 序列化(或生成代码),以便在运行时进行重构;其目的是在运行时不会被改变。

不幸的是,很多开发者不理解访问器违反了封装性。他们使用结构体而不是对象。他们认为让其他类甚至其他包依赖于类的数据成员没有任何问题。

当然,你通常需要配置你的对象实例。只是这应该通过某种配置特性完成。这可能是依赖注入容器、"BeanBox" 风格的可视化工具,或者仅仅是阅读你手动编写的 JSON、XML 或属性文件。关键是在运行时这些对象是有效地不可变的;客户端只调用它们的操作,而不访问它们的属性。


1
Holub遵循我所称的“数据隐藏”封装定义。我更喜欢使用“数据保护”。这个游戏的关键在于知道何时以及如何更改数据。只要数据本身是私有的,你就可以控制变化。此时,重要的是你是否希望它是不可变的。 - Scott Stanchfield
将类型之间的耦合最小化可以极大地提高代码的可读性和维护性。像Holub这样的权威人士以及我的亲身经历告诉我,对对象属性的外部依赖是不必要的复杂性的症状。通常有更简单、更清晰的替代方案。 - erickson

7

我喜欢让对象在构造函数中尽可能多地进行初始化,并且最小化可变的特征。

倾向于不可变的对象是一个明智的选择。然而,JavaBean 的好处在于框架/工具/库可以在运行时确定类的属性,而无需您实现特定的接口。

例如,假设您有一个 Person bean 集合,每个 bean 都有名称、年龄、身高等属性。

您可以使用以下代码按名称对这个 bean 集合进行排序:

Collection<Person> myCollection = // initialise and populate the collection
Comparator nameCompare = new BeanComparator("name");
Collections.sort(myCollection, nameCompare);

BeanComparator 类知道如何从每个对象中提取“name”属性,因为遵循了 Java Beans 约定,即您无需实现诸如以下接口的“开销”:

interface Nameable {
    public String getName();
    public void setName(String name);
}

Spring MVC中的另一个例子是一个用于存储请求URL参数的bean。可以在Web控制器(在Struts中称为“Action”)中定义如下:

public ModelAndView searchUsers(UserSearchCriteria criteria) {
    // implementation omitted
}

由于 UserSearchCriteria 预期是一个 JavaBean,如果请求 URL 包含参数,例如 maxItems=6,Spring 框架会“知道”它应该调用一个带有以下签名的方法:

void setMaxItems(int maxItems);

本质上,JavaBeans只是一种简单的约定,允许在运行时动态地发现类的属性(通常是通过工具或框架实现),当无法事先知道可能提供的属性时使用。


1
BeanComparator很糟糕。反射是用于设计时和框架的,而不是排序循环的核心。 - erickson

3
我认为最小化可变性是一件好事,正如已经指出的那样,JavaBeans的优点在于它们易于被框架处理。
为了兼顾“两全其美”,我认为一个好的选择是使用Builder模式,稍微修改Builder以符合JavaBeans标准。因此,如果你需要一个需要你的类符合JavaBeans标准的框架功能,你可以使用Builder而不是实际的类。

2
当你听到“bean”这个词时,可以期望看到某种“容器”。任何类型的JavaBean的想法都是为组件提供一个统一的接口约定,以便在运行时添加和操作。纯JavaBeans只是最简单的例子:它呈现所有接口可能性并且可序列化,这意味着您可以创建bean实例,修改它们,保存它们并重新加载它们。
很久很久以前,我编写了一个包含表示文本字符串的简单“数据库”的Java编辑器,并具有使用bean的“插件体系结构”。通过从bean存储箱中拖出bean并将其放在编辑器上,您可以向编辑器添加行为;一旦您这样做,行为(例如,在光标处转置字符的Cntl-T)就会自动在编辑器中可用。bean具有已知的接口---它们知道如何询问容器的数据结构和doSomething()方法---容器知道动态加载类文件,实例化对象并设置其访问数据库。
顺便说一下,访问器并不一定违反封装性;但是确实如此,仅因为您拥有成员,您不需要为其提供get和set方法。 JavaBean规范在这方面有点不清楚;重点是为那些需要在对象“合同”中的属性提供getter和setter。
在引入内省和反射之后并真正理解之后,这些约定的需求有所减少;在早期的Java中,您需要一个约定来查找方法。

1

Bean是这样配置的,以便自动化工具可以创建和修改Bean。它们并不一定意味着是很好的设计模式。

这些工具的示例:

Hibernate
JMX


当然。除了序列化之外,是否有任何自动化工具值得使用?有什么例子吗?谢谢! - Dave Ray
一个很好的例子就是Hibernate,它需要使用getter和setter来访问被持久化的属性。 - Brian Matthews
我认为Struts、Spring和类似的Web框架使用Beans来自动化一些网站构建过程。除非必须,否则我不会使用Beans。 - jjnguy
Hibernate 不需要使用 getter 和 setter 方法,它可以直接使用字段进行操作。只要安全管理器允许,它也可以绕过访问限制(即使用私有字段和方法)。但是,在使用 final(包括私有)API 时,一些优化可能会被禁用。 - erickson

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