2

虽然我理解实现/接口区分的价值,但我不明白为什么大多数 OO 系统在访问私有成员时会出现错误。

我确实不想在我的主程序中访问私人成员。
但我希望能够访问测试和调试。

是否有任何充分的理由发出错误而不是警告?在我看来,我被迫要么编写我可以测试的代码,但不利用接口的语言支持,或者使用语言支持,但在测试中遇到困难。

编辑

对于那些建议使用公共接口的人。可以,但不太方便。
在概念层面上,我发现不关心谁或何时相当粗略的隐私。
朋友班似乎是一个合理的解决方案。另一个可能是“所有公共”编译器开关。

4

5 回答 5

1

在我看来,我被迫要么编写我可以测试的代码,但不利用接口的语言支持,或者使用语言支持,但在测试中遇到困难。

为什么需要访问private变量和函数?它们要么在某些时候被public函数调用(但间接地),要么它们只是不可访问的代码片段,根本不应该存在,因为无法调用它们。想一想,如果完全private不可能从类外部以任何方式调用该方法,它会被运行吗?

如果你真的想测试一个private方法,你可以把它拉到它自己的类中。另外,如果它非常复杂以至于它真的需要最好地单独测试,那么它就有机会首先得到它的机会。另一种选择是在您需要/想要测试它时将其公开,但实际上并不更改“真实”代码(保留它private)。正如其他人所说,某些语言还具有通过稍微公开它们来帮助您测试这些方法的功能,例如 C++ 中的朋友、C # 中的内部Java 中的包私有。 有时 IDE 自己甚至会提供帮助。

是否有任何充分的理由发出错误而不是警告?

一个很大的原因不是你不能打电话给他们,而是其他人不能打电话给他们。想象一下,您正在编写一个将被大量客户使用的库。您已经标记了他们不需要调用的所有内容private,并且他们需要的所有功能都是公开的。程序员继续使用您的库,用它编写一堆代码,并从他们自己和他们自己的客户中产生满意的客户。

几个月后,您决定为您非常成功的库增添趣味,并发现您需要进行一些重构。因此,您 [重命名、添加/删除参数、删除] 一些private方法,但要小心保持所有公共方法的接口完全相同,以使升级过程无缝。但是在这个世界中,编译器只在您访问变量时发出警告而不是错误private,并且您的一些客户端程序员编写了调用这些private方法的代码。现在,当他们尝试升级到您的库的新版本时,他们会遇到一堆真正的错误,因为他们无法调用这些错误private方法了。现在他们要么必须花时间找出代码出了什么问题,要么重写他们不记得的大部分代码(我是否提到这是两年后的事?)。因此,他们必须完全重新学习如何使用您的库并重写他们的客户端代码,这对任何人来说都不是一件有趣的事情。现在他们很不高兴,因为你如此不体谅你,以至于你的升级真的破坏了他们所有的代码,让他们的生活变得更加困难。

猜猜看,当他们修复代码时,他们研究并调用了您的新private方法,因此如果您决定在发布升级时更改他们的界面,整个循环将重新开始。对你来说稍微方便一点的只是让你有一群不满意的客户。

等等,他们不是白痴调用我的私有方法吗?他们为什么不看警告?这是他们的错,不是我的!

嗯,是的,这是他们的错,他们本可以通过注意这些警告来防止问题的发生。但并不是每个人都是想要修复和理解警告的代码质量狂热者,而且相当多的人只是忽略了它们。问题是,如果编译器发出错误以尝试访问变量和方法而不是警告,那么您可以自己阻止整个事情private,因为否则private关键字可能根本不存在。您可能已经失去了一点时间,因为这些方法更难测试,但您已经获得了阻止不聪明的人滥用您的代码并将导致他们在路上出现的任何问题归咎于您的能力。

我最喜欢的软件开发(以及一般的产品设计)的原则之一是事情应该易于正确使用,并且很难或不可能不正确地使用。真正的私有成员就是这个建议的体现,因为它们确实使您的代码无法正确使用。

[假设性反驳:]好吧,使用我的代码的人应该足够聪明,可以弄清楚。我只要求他们多花一点时间来正确使用代码。

所以你有意识地拒绝花必要的时间来提高你的代码质量,让它更容易使用?然后我不想与你的代码有任何关系。显然,您的时间比您的客户的时间更重要,因此我将花费 2.5 秒来关闭您的项目的网页并单击下一个 Google 结果。您使用的库成员比您想象的要多得多private,而光荣的事情是您不必花费一毫秒的时间来担心它们,因为它们对您完全隐藏并且只会分散注意力从提供的更简单和更好的做事方式public界面。如果所有内容都是公开的或发出微弱警告的私有内容,那么在真正找到所需内容之前,您必须筛选更多功能。

每当您private在成员函数之前键入时,您就已经赋予自己在未来任何时候以您想要的任何方式更改它的权力,因为除了您之外没有人可以触摸它。第二次其他人尝试访问它时,他们将收到一个显示停止错误,因为编译器支持你,并且当你已经以更可用的形式完美地提供了他们需要的一切时,他们不会让他们对你的代码做任何愚蠢的事情在您的public界面中。

是的,它会让现在的测试变得稍微困难​​一些,但它也确保了你将来在重构时不必担心它,并使你的代码容易被其他人使用。继续并暂时将其公开(我有点喜欢您的“全公开编译器切换”想法 :),但是当您完成后不要忘记将其切换回来,您和您的客户都可以享受使用更简单和更简单的工作的乐趣更具适应性的代码。:D

于 2012-06-24T01:03:39.183 回答
0

显而易见的原因是太多人似乎忽略了许多(全部?)警告。在某些语言(例如,Python)中,它几乎就像您所说的那样——“私有”的东西基本上是反对外部代码直接使用它的建议,但编译器并没有强制执行。

至于这有多大意义,我怀疑它因语言而异——在诸如 C++ 之类的语言中,对它们的态度(“防止墨菲,而不是马基雅维利”)可以被视为将其作为警告而不是错误的理由。

我认为可以肯定地说,在 Ada 中,至少可以说,这会得到更冷的接受(这并不是说我认为 C++ 程序员也会热情地接受它,只是他们可能不太讨厌这个想法和大多数 Ada 程序员一样)。

另一方面,我不得不怀疑一个无法通过其外部接口进行测试(至少相当好)的类的设计。在你做不到的罕见(无论如何应该是罕见的)场合,我认为让测试类成为朋友(用 C++ 的说法,尽管许多其他人有类似的概念)是相当容易证明的。

于 2012-06-24T00:35:23.113 回答
0

完全封装有几个优点:

  • 安全。具有强封装性的强类型OOP语言可以对程序中数据的安全性有一定的保证。Java 语言的设计考虑了安全性,因此某些库类(例如,StringSecurityManager)不能访问其字段。这可以防止恶意代码对这些对象做坏事,并允许代码假定对象是安全的。

  • 可维护性private保留字段和方法的主要原因之一private是允许实现无缝更改;只要没有对公共接口进行更新,使用更新的类的代码就可以在没有任何更改的情况下工作。如果您允许访问private字段然后更改实现,则可能会破坏无限量的代码。

  • 稳定性/可验证性/可测试性。类通常在其字段上施加不变量 - 例如,动态数组的实现可能需要跟踪使用多少空间的字段实际上对应于元素的总数。允许人们任意访问private字段,即使有警告,也可以打破这些不变量。如果不能依靠不变量,就很难或不可能推断代码的正确性。此外,如果您确实在代码中的某处破坏了不变量,则可以想象,您必须查看程序中可以访问该对象的每一段代码,因为它们中的任何一个都可能正在访问private场地。通过强封装,这些不变量不会被破坏,并且通过friends 或包私有机制的半封装,查看的代码量是有限的。

至于您关于测试的问题 - 许多语言允许在某些情况下破坏封装;C++ 有friend,Java 有 package-private 等,所以类可以说“通常你不能碰这些,但可以做例外”。然后,您可以将测试代码friend与主类放在同一个包中,以便更彻底地对其进行测试。

希望这可以帮助!

于 2012-06-24T00:35:26.590 回答
0

我看到的方式是,您需要忘记访问对象中的任何内容,除非您有办法在该对象的接口中执行此操作。如果您尝试直接访问特定于实现的私有成员,我认为正确的 OO 系统应该发出错误(而不是警告)。我最近参加了 Kevlin Henney 关于这个主题的精彩演讲,我发现它非常有用,可以在此处查看副本:http: //www.infoq.com/presentations/It-Is-Possible-to-Do-OOP- in-Java(请注意,主要是关于 java,但也包括与其他 OO 系统的比较)

对于大多数时间的测试,我发现大部分被测代码都被公共接口调用覆盖。只有在极少数情况下,我才需要使用运行时反射之类的东西来获得绝对 100% 的覆盖率。

于 2012-06-24T00:43:33.963 回答
0

我正要发帖,“严格执行封装可以防止你的老板踩到你的私人成员。” 直到我意识到这听起来可能是错误的,但转念一想这可能是对的。

于 2012-06-24T00:57:29.830 回答