我将一个不可变数据模型类标记为final
,以确保更改其值的唯一方法是创建一个新实例。 (不幸的是,这些字段不能是最终的,因为它们需要由 Hibernate 填充。)
这很有效,直到我想检查另一个类在使用模型的无效实例调用时是否抛出正确的异常。模型的构造函数会验证参数,因此必须使用反射来设置字段。这是非常笨拙的,因为模型有相当多的字段,并且字段名称必须进行硬编码。
我也无法 mock 该模型,因为它是最终的。 (是否应该使用接口(interface)来启用模拟,同时保持类不可变,是否也存在争议。通过使用接口(interface),就无法以编程方式强制方法在实例的整个生命周期中必须返回相同的值。)
在这种情况下人们通常会做什么?有没有任何标准方法?
最佳答案
一般来说,您不应该想要模拟数据对象。数据对象应该没有逻辑,也没有外部依赖关系,因此模拟对象并没有多大用处。相反,可以非常轻松地创建假实例,您可以根据需要将其填充到方法中。
此外,还有一些其他原因您可能希望避免将 Hibernate 持久对象视为不可变:
- Hibernate 提供的对象本质上是 not thread-safe因此失去了不可变值对象通常提供的线程安全优势。
- 您可能会发现您的对象实际上是 proxies ,可能会削弱
最终
语义。 - Hibernate 控制的对象的操作方式完全不同 whether their session is still open (附加与分离)使它们成为不可变对象(immutable对象)的非常糟糕的选择。如果您的不可变对象(immutable对象)取决于 session 生存期,那么它并不是真正不可变的。
- 听起来有些对象在应用程序层可能有效或无效,超出了数据库层验证的范围。这使得封装您的验证问题变得有点困难。
- 您需要有一个公共(public)的无参数构造函数,这与不可变值对象的典型实例控制类型相反。
- 因为对象本质上是可变的,所以 override equals and hashCode 就更复杂了.
我的建议?如果您需要比 Hibernate DAO 所能提供的更多的不变性和数据验证保证,那么创建一个带有 Final 字段(或私有(private)构造函数和静态工厂方法)的真正的最终不可变类,然后创建一个构造函数(或静态工厂方法)从 Hibernate DAO 复制值。
如果您决定此选项,您将面临两个大致并行更改的数据对象的开销,但您也可以获得分离关注点的好处(以防 Hibernate 对象发生分歧) )以及真正不可变、覆盖 equals-and-hashcode、 session 无关、保证有效的对象的易用性,您可以轻松地为测试创建这些对象。
关于java - 如何编写一个测试友好的不可变值类?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13223337/