【问题标题】:Should an immutable class member have an accessor method or allowed to be public?不可变的类成员是否应该具有访问器方法或允许公开?
【发布时间】:2019-01-30 14:38:17
【问题描述】:

我有一个关于最佳设计实践的问题。我一直在尝试在我的项目中构建更多不可变的组件,因为我读到它们从长远来看更容易维护,并且想对此进行测试。

当你有一个具有不可变数据成员的类时,说

 public/private final int importantNumber = 3;

是否仍应将 int 声明为私有并赋予访问器方法,或者因为它的 final,允许直接访问并将其公开有什么困难?由于您无法对其进行更改,因此我认为将其公开没有任何问题。

在我的例子中,不可变对象是使用传递给它的值创建的,然后会从数据库中自行填充其他对象,一切都是最终的。

或者这仅仅是一个偏好问题,会引发一场宗教战争?由于有很多关于从类内部访问数据成员的问题,我想明确表示我是在向另一个试图获取值的外部类询问。

【问题讨论】:

  • 访问器方法有可能包含更多逻辑(计算等),而字段没有,使用方法还允许您重命名字段(因为它仅在内部使用)
  • 我总是使用 getter,因为它可以在单元测试中轻松模拟。
  • @NeplatnyUdaj 但这可能是“包保护”。如果它是公开的,您将无法控制还有谁会调用它,至少在 Java 9 和模块之前是这样。
  • @GhostCat 你的观点已经死了,我只考虑了这里和我,我没有考虑过以后谁可能会使用代码。目标是易于维护,所以虽然我对返回值并且什么都不做的 getter 感到畏缩,但它们确实有自己的位置。太糟糕了,没有可以包含的关键字让我声明一个伪作用域和一个 getter 的名称。私有int重要编号(公共getImportantNumber)= 3;然后,如果需要,我可以稍后定义 getter。

标签: java immutability private public accessor


【解决方案1】:

将事物公开允许其他代码依赖它。

数据是可变的还是不可变的都没有关系。一旦其他代码使自己依赖该信息,您就可以解决未来的问题。在任何更大的项目中,这些问题迟早会显现出来。

突然之间,(内部)实现细节可能会泄露到其他代码中!当然:如果该数据是可变的,那只会打开一个真实的蠕虫罐。

但即使是简单的if (someObject.someField == whatever) then do this else do that 也会很快变成一个大问题。假设您在大型项目的 10、50、100 个不同的地方拥有该代码。

因此,您的默认规则是:隐藏您的信息。 公开例外,它的发生是因为你绝对希望你的设计是这样的。您的默认是为您的字段(甚至方法)提供最严格的可见性,仍然允许您实现您的要求。

请注意:隐藏字段并为其设置访问器方法会“稍微”好一些。因为if (someObject.someMethod() == ...) 会导致同样的问题。而且(几乎)更糟:现在你决定someMethod() 应该做一些不同的事情来解决一个特定的问题。但是您确定该方法调用的其他 99 种用法可以与该更改一起正常工作吗?!

最后的警告:Java 9 引入了模块概念,因此您现在终于可以拥有一些公共的东西,但仍然不能从您的模块外部使用。但这更像是一个理论上的附录,我仍然会遵循基本规则,除非我有充分的理由这样做,否则不要公开任何事情。如果您想访问该信息,例如单元测试,那么应该使用受包保护的 getter。

【讨论】:

  • 您的第一行实际上已经说明了一切。我没有想到。所以希望我有一个来自 C++ 的正确数据结构。类应该是活的东西,但有时我只需要一些东西来保存数据。
  • @Fering 对于“真实”数据类,情况略有不同。如果它“只是”一个“记录”(或 java 中的 bean),那么带有公共字段的普通类可能是可以接受的。直到 2 年后你进来,发现你需要别的东西,然后开始重新设计所有的直接访问。去过那里,做到了。
  • 这就是为什么在我阅读您的评论后我回去,将公共更改为私人,并添加访问器,现在重新编写代码。但是不做豆子。
【解决方案2】:

将 final 字段声明为公共或使用公共 getter 将其私有化之间没有实际区别

关于为什么拥有公共领域真的很糟糕,有很多理论性的争论,但实际上没有真正的实际理由不这样做。是的,理论上您可以稍后更改 getter 以执行不同的操作,但您可能不会。即使你想大多数人都会告诉你,getter 应该只返回它获得的字段,而不需要额外的逻辑。

为了更清楚,这两个选项同样错误。字段不应该是公开的,也不应该有吸气剂。 但是,如果你做其中一个,另一个同样是错误的。希望对您有所帮助:)

【讨论】:

  • 有一个:你总是可以在 3 年后进来,将方法体更改为 throw new RuntimeException("you shouldnt have called this method, the javadoc told you so, burn mf burn"); ...当然,我们永远不会这样做,对吧。
  • @GhostCat 呵呵 :) 别看我,不是我 :)
  • @Robert 是的,我读过的一篇文章也提到 getter 和 setter 是邪恶的,应该避免。我只是不能阻止自己在所有情况下都使用它们。
  • 我记得当我第一次了解 Bertrand Meyer 和 martinfowler.com/bliki/UniformAccessPrinciple.html 时......我想:哦,那是很合理的,区分 x.foo 和 x .foo() ...但是,25 年后,也许我对这些事情太老了。
  • “将 final 字段声明为 public 或使用 public getter 将其私有并没有实际区别。” ...除了制作不可变字段这一事实之外public 将 API 与实现联系起来。一旦分发,您的类必须永远支持该字段,即使稍后会首选其他一些实现。在某些情况下,公开字段是正确的做法(例如,公开枚举类中的常量),但公开字段总是不鼓励松散耦合。
【解决方案3】:

当您声明一个对象final 是不可变的并且没有人可以更改它的值时。

但是根据OOP中的封装原则定义一个重要的公共或私有对象,您必须声明一个获取该字段的方法,直到稍后您可以轻松跟踪该对象需要的位置以及哪个类需要该值。

【讨论】:

  • 你的推理是错误的。我的 IDE 可以准确地告诉我 x.someField 或 x.someMethod() 的用途。唯一的区别是:当您公开一个方法时,您可以在调用者不注意的情况下更改该方法的行为。这可能会导致一系列其他问题。 以后更改代码是你尽量避免的事情......
猜你喜欢
  • 2015-10-26
  • 1970-01-01
  • 1970-01-01
  • 2012-05-03
  • 2021-05-08
  • 1970-01-01
  • 1970-01-01
  • 2014-01-26
  • 2018-08-29
相关资源
最近更新 更多