Java Beans作为数据存储类设计不好吗?

通常JavaPractices.com是一个很好的想法的网站,但是这个让我很烦恼: JavaBeans很糟糕 。

本文引用了几个原因,主要是JavaBean这个术语的意思是“Java Bean是一个可以在构建器工具中直观操作的可重用软件组件”。 不是数据存储,违反了某些模式,而且更复杂。

现在我同意最后一个,但在我看来,列表中的JavaBeans比嵌套的地图更有意义。 文章声称数据库映射框架应该调用构造函数,而不是set *方法,并且对象应该是不可变的。 但是在我看来,在尝试构建对象时调用set *方法比new MappedObject("column1", "column2", "yet another column", "this is stupid");更容易阅读new MappedObject("column1", "column2", "yet another column", "this is stupid");

除了数据库映射之外,我还使用JavaBean样式类进行其他操作,例如对于IRC bot,每个用户都有一个对象,可以使用各种内容进行更新。 我不希望每次给出新信息时都创建一个新对象,我想将它添加到现有的对象中。

所以我的问题是:使用JavaBeans进行数据存储是一种不好的做法,应该避免使用,还是非常安全?

你似乎在误读文本。

现在我同意最后一个,但在我看来,列表中的JavaBeans比嵌套映射更有意义

该文本从未提及嵌套地图作为替代(yiack)

…应该调用构造函数,而不是设置*方法,并且对象应该是不可变的

这是一个很好的做法,在处理线程时特别有用。

但我们不能说使用setter也是baaad ,特别是当一个线程正在使用该对象时。 那是非常安全的。

我不希望每次给出新信息时都创建一个新对象,我想将它添加到现有的对象中。

没关系,只要你控制对象没有问题,其他一些人可能会发现更容易创建一个新对象。

使用JavaBeans进行数据存储是一种不好的做法,应该避免使用,还是非常安全?

不,这不是一个坏习惯。 也不是很安全 。 取决于情况。

可变对象(不是JavaBeans本身)的问题是使用不同的线程来访问它们。

您必须同步访问权限以避免一个线程修改对象而另一个线程正在访问它。

不可变对象没有这个问题,因为……好吧,它们不能改变,因此,你不必同步任何东西。

要确保对象是不可变的,您必须将属性声明为final。

 class MyBean { private final int i; } 

如果要为MyBean.i分配合理的值, MyBean.i必须在构造函数中指定它:

  public MyBean( int i ) { this.i = i; } 

由于变量是final,因此您无法使用setter。 你可以提供一个吸气剂。

这是完全线程安全的,最好的是,你不必同步访问,因为如果两个线程试图获取i的值,它们将始终看到在实例化时分配的值,你没有同步任何东西。

不是不好的做法或良好的做法。 我们必须使用单个线程,即使在像servlet这样的multithreading环境中也是如此。

如果您将来必须处理multithreading应用程序,您可以考虑使用不可变的JavaBean;)

BTW,创建不可变bean的替代方案,并且仍然提供一堆setter,使用的Builders如下:

  Employee e = new EmployeeBuilder() .setName("Oscar") .setLastName("Reyes") .setAge(0x1F) .setEmployeeId("123forme") .build(); 

这看起来非常类似于常规bean中使用的常规setXyz,具有使用不可变数据的好处。

如果需要更改一个值,可以使用类方法:

  Employee e = Employee.withName( e, "Mr. Oscar"); 

它采用现有对象,并复制所有值,并设置一个新的….

  public static EmployeeWithName( Employee e , String newName ){ return new Employee( newName, e.lastName, e.age, e.employeeId ); } 

但同样,在单线程模型中使用getter / setter是完全安全的。

PS我强烈建议你购买这本书: Effective Java 。 你永远不会后悔,你将有信息来判断更好的文章,如引用。

我反对使用JavaBeans作为数据存储类是因为它们允许状态不一致的对象。 在这种bean的典型用例中,您有以下步骤:

  • 实例化该类;
  • 设置第一个属性;
  • 设置第二个属性;
  • 设置最终属性;
  • 使用对象实例。

现在你的class级已准备就绪。 那么这里的问题是什么? 在实例化类和设置最终属性之间,你有一个处于内部不一致或不可用状态的对象,但没有什么能阻止你意外地使用它。 我更喜欢一个系统,在实例化时类会自动处于一致的可用状态。 出于这个原因,我更喜欢传递构造函数中的所有初始状态,或者,如果所述初始状态太复杂,则以哈希映射或集合等forms传递初始状态。 用例场景现在是:

  • (可选:设置参数对象) ;
  • 实例化该类;
  • 使用对象实例。

在这个工作流程中,我可能无意中开始使用状态不一致的对象。 如果我使用一个参数对象,它根本不直接用于任何东西,它的内容将在我的主类实例化时进行审查。 从实例化返回时,主类本身将为我提供一个立即使用的对象实例。

当然,这种设置最适合较简单的对象。 对于包含可选属性等更复杂的对象,您需要更进一步,并使用其他人指向您的Builder模式。 当你有更复杂的场景,IMO时,构建器很好,但是对于更简单,更直接的参数化,仅仅使用构造函数参数或某种类型的参数对象就足够了。

“JavaBeans模式存在严重缺陷。” – Joshua Bloch,Effective Java

我爱那些家伙。 脱离上下文任意引用已经成为我不相信这样的文章的理由。

BTW,推荐书(Effective Java)对这两种模型,它们的优点,缺点和替代方案进行了广泛的讨论。 你可能想看一下。 但JavaBeans没有任何本质上的错误,有时它们不是最好的选择。

编辑:请参阅Google图书上的有效Java中的第2项(“在面对许多构造函数参数时考虑构建器”): http : //books.google.com/books?id = ka2VUBqHiWkC&lpg = PP1&g = PA11 #v = onepage&q&f = false

避免制定者的主要原因是不变性。 预先设定不变性代码,避免围绕这些对象出现任何线程问题。

如果你最终得到一个读取的构造函数

新人(“param 1”,“param 2”,“param 3”,“param 4”,“param 5”,“param 6”,“param 7”,“param 8”)

那你的对象太复杂了。 您将需要Parameter对象(请参阅Martin Fowler重构书籍)。

在开始时防御性地编码,并且来到并维护代码的人要么感谢(好)要么诅咒你(因为他们不能懒惰而只是改变对象)。

当您需要更改对象时,请添加复制构造函数(即克隆方法)。 现代JVM可以轻松快速地处理这个问题,并且几乎没有速度惩罚。 您还可以使Hotspot和GC变得简单。

它非常安全,远比无限嵌套的java.util.Map更可取。 您可以获得类型安全性,更容易理解代码,并避免您的代码最终出现在每日WTF上。 请注意,实现应该是隔离的 – 不要将太多逻辑(超出简单validation)混合到数据存储类中。 您应该阅读有关POJO的信息

文章的意图很有意义,但在实践中,避免豆类和制定者很难维护。 例如,在许多情况下,框架使用构造函数是不可能的,因为名称通常是标识function,而方法/构造函数参数名称不在.class文件中维护。 (虽然至少有一个库可以解决这个问题。)

塞特斯是下一个最好的东西 – 不是“纯粹的”OO,而是对施工人员的巨大便利并且在实践中运作良好。

由于“坏”的java bean,Whem测试开始失败,然后我会重新考虑它引起关注。 但到目前为止我还没有看到。 唯一一次我说他们可能不合适的是在multithreading代码中共享bean,因为同步共享的可变状态是棘手的。 避开这一点,豆子就好了。