静态语言的隐私

时间:2012-06-24 00:27:59

标签: oop unit-testing language-agnostic private language-design

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

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

有没有任何理由发出错误而不是警告?我看到它的方式,我被迫编写我可以测试的代码,但是它没有利用语言支持接口,或者使用语言支持,但是测试有困难。

修改

对于那些建议使用公共接口的人。你可以,但它不那么有意义 在概念层面上,我发现隐私并不关心谁或何时非常粗糙 朋友类似乎是一个合理的解决方案。另一个可能是所有公众'编译器开关。

5 个答案:

答案 0 :(得分:1)

  

我看到它的方式,我不得不编写我可以测试的代码,但是它没有利用语言支持接口,或者使用语言支持,但是测试有困难。

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

如果你真的想要测试一个private方法,你可以把它拉到自己的类中。另外,如果它非常复杂以至于它确实需要单独进行最佳测试,那么它有可能首先获得机会。另一种选择是在需要/想要测试它时公开它,但实际上并没有改变真正的'代码(留下private)。正如其他人所说,某些语言也有一些功能可以帮助您通过稍微暴露它们来测试这些方法,例如C ++中的朋友,internal in C#package-private in Java. Occasionally the IDE's themselves even help out.

  

有没有任何理由发出错误而不是警告?

一个重要原因并非如此,您无法呼叫它们,因此其他人无法呼叫它们。想象一下,您正在编写一个将被大量客户使用的库。您已经标记了他们不应该拨打private所需的所有内容,他们所需要的所有功能都是公开的。程序员继续开始使用您的库,用它编写一堆代码,并为他们自己和他们自己的客户创造满意的客户。

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

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

  

等等,他们不是因为私人方法而打电话的?为什么他们没有看警告?这是他们的错,不是我的!

嗯,是的,这是他们的错,他们可以通过注意那些警告来防止这个问题。但并不是每个人都是一个想要修复和理解警告的代码质量狂热分子,而且很多人都会忽略它们。问题是,如果编译器因尝试访问private变量和方法而不是警告而发出错误,您本可以自行阻止整个事情,否则private关键字可能根本不存在。你可能已经失去了一点时间,因为这些方法难以测试,但是你已经获得了让不那么聪明的人不会滥用你的代码并责备你造成他们未来的任何问题的能力。

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

  

[假设的反驳:]好吧,使用我的代码的人应该足够聪明才能弄明白。所有我要求他们到期的只是花一点额外的时间来正确使用代码。

因此,您有意识地拒绝花费必要的时间来提高代码质量并使其更易于使用?然后,我不想要与你的代码有什么关系。显然,您的时间比您的客户更重要,因此我需要2.5秒才能关闭项目的网页并点击下一个Google结果。你使用的图书馆中有private个成员比你想象的要多得多,而光荣的是,你不必花费甚至一毫秒的时间来担心他们,因为他们会这样做。完全隐藏起来,只会分散public界面提供的更简单,更好的做事方式。如果所有内容都是公开的或悄悄发布警告,那么在您真正找到所需内容之前,您必须筛选更多的功能。

无论何时在成员函数之前键入private,您都有权在将来的任何时候以任何方式更改它,因为除了您之外没有人可以触摸它。第二个人试图访问它,他们将得到一个停止显示的错误,因为编译器有你的背,并且当你已经提供了他们需要的所有东西时,他们不会让他们对你的代码做任何愚蠢的事情。 public界面中更实用的表单。

是的,它会让现在的测试稍微困难一些,但它也确保你在将来重构并使你的代码成为批次时不必担心它/ em>更容易让其他人使用。来吧,暂时公开(我有点像你的公共编译器开关想法:),但是当你完成了你并且你的客户可以全部时,不要忘记将其切换回来使用更简单,更具适应性的代码,让您感到高兴。 :d

答案 1 :(得分:0)

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

至于有多大意义,我怀疑它在语言之间有所不同 - 比如C ++,对他们的态度(“防止墨菲,而不是马基雅维利”)可以被视为理由因为它是警告而不是错误。

我认为可以肯定地说,在Ada中,至少可以接受很多更酷的接收(而且这并不是说我认为C ++程序员会热情地接收它) ,只是他们可能不会像大多数Ada程序员那样讨厌这个想法。

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

答案 2 :(得分:0)

完全封装有几个好处:

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

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

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

至于你关于测试的问题 - 许多语言允许在某些情况下破解封装; C ++有friend,Java有包私有等,所以这个类可以说“通常你不能触摸这些,但可以做出例外”。然后,您可以将测试代码设置为friend或与主类在同一个包中,以便更彻底地测试它。

希望这有帮助!

答案 3 :(得分:0)

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

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

答案 4 :(得分:0)

我即将发布,“强大的封装强制执行使你的老板不会踩到你的私人成员。”直到我意识到这听起来有多么糟糕,但是第二次想到它是 可能只是对了。