Sti*_*ube 63 java immutability value-objects
最近遇到了不可变对象的概念,我想知道控制状态访问的最佳实践.即使我的大脑的面向对象部分让我想要在公众成员的视线中畏缩,但我认为没有类似这样的技术问题:
public class Foo {
public final int x;
public final int y;
public Foo( int x, int y) {
this.x = x;
this.y = y;
}
}
Run Code Online (Sandbox Code Playgroud)
我觉得更适合声明字段private
并为每个字段提供getter方法,但是当状态明确只读时,这似乎过于复杂.
提供对不可变对象状态的访问的最佳实践是什么?
Kev*_*man 62
这完全取决于你将如何使用该对象.公共领域本质上并不是邪恶的,将一切都公之于众是不好的.例如,java.awt.Point类使其x和y字段成为公共字段,它们甚至不是最终字段.您的示例似乎很好地使用了公共字段,但是您可能不希望再公开另一个不可变对象的所有内部字段.没有包罗万象的规则.
Pau*_*thy 31
我在过去的想法一样,但通常最终将变量设为私有并使用getter和setter,以便稍后我仍然可以选择在保持相同的界面的同时对实现进行更改.
这让我想起了我最近在Robert C. Martin的"清洁代码"中读到的一些内容.在第6章中,他给出了一个略微不同的观点.例如,在第95页他说
"对象隐藏了抽象背后的数据,并公开了对该数据进行操作的函数.数据结构暴露了他们的数据,没有任何有意义的功能."
在第100页:
豆类的准封装似乎使一些OO纯粹主义者感觉更好但通常没有提供其他好处.
根据代码示例,Foo类似乎是一个数据结构.因此,根据我在清洁代码(不仅仅是我给出的两个引号)中的讨论所理解的内容,该类的目的是公开数据,而不是功能,并且让getter和setter可能没有多大帮助.
同样,根据我的经验,我通常会继续使用私有数据的"bean"方法与getter和setter.但话说回来,没有人要我写一本关于如何编写更好的代码的书,所以也许Martin有话要说.
Mar*_*nik 11
如果您的对象具有本地足够的用途,以至于您不关心将来如何破坏API更改的问题,则无需在实例变量之上添加getter.但这是一个通用主题,不是特定于不可变对象.
使用getter的优势来自于一个额外的间接层,如果您正在设计一个将被广泛使用的对象,并且其实用性将延伸到不可预见的未来,这可能会派上用场.
无论不变性如何,您仍然会暴露此类的实现.在某个阶段,您需要更改实现(或者可能产生各种派生,例如使用Point示例,您可能需要使用极坐标的类似Point类),并且您的客户端代码会暴露给此.
上面的模式可能很有用,但我通常将它限制为非常本地化的实例(例如,传递信息元组 - 我倾向于发现看似无关的信息的对象,或者是不好的封装,或者信息是相关的,我的元组转换成一个完全成熟的对象)
要记住的重要一点是函数调用提供了一个通用接口.任何对象都可以使用函数调用与其他对象交互.您所要做的就是定义正确的签名,然后离开.唯一的问题是你必须通过这些函数调用进行交互,这些调用通常很有效,但在某些情况下可能很笨拙.
直接暴露状态变量的主要原因是能够直接在这些字段上使用原始运算符.如果做得好,这可以增强可读性和便利性:例如,添加复数+
,或访问键控集合[]
.如果您使用语法遵循传统约定,那么这样做的好处可能会令人惊讶.
问题在于运营商不是通用接口.只有一组非常特定的内置类型才能使用它们,这些只能以语言所需的方式使用,而且您无法定义任何新的类型.因此,一旦使用基元定义了公共接口,就可以锁定自己使用该基元,只使用该原语(以及其他可以轻松转换为基础的东西).要使用其他任何东西,每次与它进行交互时都必须围绕该原语跳舞,并且从干燥的角度杀死你:事情会很快变得非常脆弱.
有些语言使运算符成为通用接口,但Java则不然.这不是对Java的起诉:它的设计者故意不选择包含运算符重载,他们有充分的理由这样做.即使你正在处理那些似乎与传统操作符合适的对象,让它们以一种实际上有意义的方式工作也会有惊人的细微差别,如果你没有完全指出它,那么你将会以后付钱.使基于函数的界面可读和可用通常要比通过该过程容易得多,并且您通常比使用运算符时获得更好的结果.
还有人参与这一决定权衡,但是.有是时候操作员为基础的界面确实工作比基于函数的一个更好的,但没有操作符重载,该选项只是不可用.无论如何都要试着挑战操作员,这会让你陷入一些你可能并不想真正想要的设计决定.Java设计者认为这种权衡是值得的,他们甚至可能对此有所了解.但是这样的决定并非没有一些影响,而这种情况就是影响力下降的地方.
简而言之,问题不在于暴露您的实现本身.问题是将自己锁定在该实现中.