私人与受保护 - 可见性良好实践关注

Mad*_*iha 137 language-agnostic oop coding-style

我一直在寻找,我知道理论上的差异.

  • public - 任何类/函数都可以访问方法/属性.
  • protected - 只有此类和任何子类可以访问方法/属性.
  • private - 只有此类可以访问方法/属性.它甚至不会被继承.

这一切都很好,问题是,它们之间的实际区别是什么?你什么时候使用private,何时使用protected?这个是否有标准或可接受的良好做法?

到目前为止,为了保留继承和多态的概念,我使用public任何应该从外部访问的东西(比如构造函数和主类功能),以及protected内部方法(逻辑,辅助方法等).我是在正确的轨道上吗?

(请注意,这个问题适合我,但也可供将来参考,因为我还没有看到像这样的问题).

JB *_*zet 128

不,你没有走上正轨.一个好的经验法则是:尽可能将所有内容都设为私有.这使您的类更加封装,并允许更改类的内部,而不会影响使用您的类的代码.

如果您将类设计为可继承的,那么请仔细选择可以从子类覆盖和访问的内容,并使其受到保护(最后,如果您希望使Java可访问但不可覆盖,则可以使用Java).但请注意,只要您接受具有类的子类,并且存在受保护的字段或方法,此字段或方法就是该类的公共API的一部分,并且在不破坏子类的情况下可能不会在以后更改.

不应该继承的类应该是最终的(在Java中).为了进行单元测试,您可以放松一些访问规则(私有到受保护,最终到非最终),然后记录它,并明确表示尽管该方法受到保护,但它不应该被覆盖.

  • 好吧,问题不是用户*想要覆盖的内容,问题是你想要*允许*被覆盖的问题.通常它有助于切换边并尝试思考:如果我使用该类并创建了一个子类,我希望能够覆盖什么?有时其他用户仍会遗漏事情...... (15认同)
  • 典型的控制怪胎意见. (4认同)
  • **受保护的字段是继承中的不良做法!**.请小心.[这里](http://stackoverflow.com/questions/3182653/are-protected-members-fields-really-that-bad)是为什么 (3认同)
  • **私人领域在继承方面做得不好!.**(过分夸大)请阅读我的答案. (3认同)
  • 这里真正的问题是关于“私有”与“受保护”的问题,我什么时候希望财产被继承,什么时候不希望继承?我真的无法判断用户将来是否想参加我的课程并扩展它...... (2认同)

Nic*_*ick 50

让我先说一下,我在这里主要谈论方法访问,并在较小程度上标记类final,而不是成员访问.

古老的智慧

"除非你有充分的理由不将其标记为私人"

在开源管理开发人员库空间和VCS /依赖管理之前,在写入的日子里有意义.由于Github,Maven等原因,它变成了超级协作.当时,通过限制图书馆的使用方式也可以赚钱.我职业生涯的前8或9年大概坚持这种"最佳实践".

今天,我认为这是一个糟糕的建议.有时候有一个合理的论据来标记一个方法私有,或一个类最终,但它是非常罕见的,即使这样,它可能没有改进任何东西.

你有没有:

  • 感到失望,感到惊讶或受到图书馆等的伤害,这些图书馆有一个可以通过继承和几行代码修复的错误,但由于私有/最终方法和类被迫等待可能永远不会出现的官方补丁?我有.
  • 想要使用一个与用户想象的略有不同的用例的库,但由于私有/最终的方法和类而无法这样做?我有.
  • 对图书馆等感到失望,感到惊讶或受到伤害,因为它的可扩展性过于宽松?我还没有.

这些是我在默认情况下将方法标记为私有的三个最大合理化:

合理化#1:它不安全,没有理由覆盖特定的方法

我无法计算我是否有必要覆盖我编写的特定方法的错误次数.我曾经在几个流行的开源库中工作,我很难学会将事物标记为私有的真正成本.它通常会消除解决问题或用例的唯一实用解决方案.相反,我从未在16年多的专业发展中遗憾地因为与API安全相关的原因而标记了一种受保护而非私有的方法.当开发人员选择扩展一个类并覆盖一个方法时,他们会有意识地说"我知道我在做什么".为了生产力,应该足够了.期.如果它是危险的,请注意它在类/方法Javadocs中,不要盲目地关上门.

默认保护的标记方法是对现代软件开发中的一个主要问题的缓解:想象力的失败.

合理化#2:它保持公共API/Javadocs干净

这个更合理,并且取决于目标受众甚至可能是正确的事情,但是值得考虑保持API"干净"的成本实际上是:可扩展性.由于上面提到的原因,为了以防万一,标记受保护的东西可能更有意义.

合理化#3:我的软件是商业化的,我需要限制它的使用.

这也是合理的,但作为消费者,我每次都会选择限制较少的竞争对手(假设不存在明显的质量差异).

永远不要把话说绝了

我不是说永远不会将方法标记为私有.我说更好的经验法则是"除非有充分的理由不让方法受到保护".

这个建议最适合那些已经分解为模块的图书馆或大型项目的人.对于较小或较多的单片项目,由于您无论如何都要控制所有代码,因此不太重要,如果需要,可以轻松更改代码的访问级别.即便如此,我仍然会给出同样的建议:-)

  • 使方法可覆盖所必需的关键字的组合是语言细节恕我直言.我向理性化#1提出的反驳论证正是针对你提到"如果我不太确定......"的情况.实际上,如果你不能想出它为什么会有危险的原因,那么通过选择可扩展性就可以获得更多.当你说'关联'时,我认为它是作文的同义词,在这种情况下,我认为这两种方式都不重要. (4认同)
  • 我不记得仅仅因为我想覆盖1或2或方法而不得不“克隆”基础类几次。就像您所说的那样,随着社会趋势,我们共享比以往任何时候都更多的代码,默认情况下,使用受保护比私有更有意义。即对自己的逻辑更开放。 (3认同)

Lar*_*rry 20

停止滥用私人领域!

这里的评论似乎绝对支持使用私有领域.那么,我有不同的想法.

私人领域原则上是好的吗?是.但是,当你不确定肯定 是错的时候,黄金法则会让一切都变得私密!在遇到问题之前,你不会看到问题.在我看来,如果你不确定,你应该将字段标记为受保护.

您希望扩展一个类有两种情况:

  • 您想要为基类添加额外的功能
  • 您想要修改当前包之外的现有类(可能在某些库中)

在第一种情况下,私有字段没有任何问题.人们滥用私人领域这一事实让你感到非常沮丧,当你发现你无法修改狗屎时.

考虑一个模拟汽车的简单库:

class Car {
    private screw;
    public assembleCar() {
       screw.install();
    };
    private putScrewsTogether() {
       ...
    };
}
Run Code Online (Sandbox Code Playgroud)

图书馆作者认为:我的图书馆用户没有理由需要访问assembleCar()正确的实施细节吗?让我们把螺丝标记为私有.

好吧,作者错了.如果你只想修改assembleCar()方法而不将整个类复制到你的包中,那你就不走运了.你必须重写自己的screw领域.让我们说这辆车使用了十几个螺丝,每个螺丝都涉及一些不同的私人方法中的一些重要的初始化代码,这些螺丝都标记为私有.在这一点上,它开始吮吸.

是的,你可以和我争辩说,图书馆作者可以编写更好的代码,所以私有字段没有任何问题.我并不是说私人领域是OOP的问题.当人们使用它们时,这是一个问题.

这个故事的寓意是,如果你正在写一个图书馆,你永远不知道你的用户是否想要访问一个特定的领域.如果你不确定,请记下它protected以便以后每个人都会更开心.至少不要滥用私​​人领域.

我非常支持尼克的回答.

  • 使用私有字段通常是说继承是错误的抽象,它会改变类/对象的某种行为(如果有意识地使用)。在不更改API的情况下,更改行为通常会默默地违反LSP。好的答案,+ 1。 (2认同)

Anu*_*rag 16

我刚才读了一篇文章,谈到尽可能地锁定每个班级.除非您迫切需要向外界公开某些数据或功能,否则请将所有内容设为最终和私有.在以后扩展范围总是很容易,但不是相反.首先考虑制作尽可能多的东西,final这样可以在两者之间进行选择,private并且protected更容易.

  1. 除非您需要立即对它们进行子类化,否则请将所有类设为final.
  2. 除非您需要立即子类化并重写它们,否则请将所有方法设为final.
  3. 使所有方法参数都是最终的,除非你需要在方法的主体内更改它们,这在任何时候都是有点尴尬的.

现在,如果你留下最后一堂课,那么除非世界绝对需要一些东西,否则将一切都变为私人 - 让它公之于众.

如果你留下了一个确实有子类的类,那么仔细检查每个属性和方法.首先考虑是否要将该属性/方法公开给子类.如果你这样做,那么考虑一个子类是否会对你的对象造成严重破坏,如果它在覆盖过程中弄乱了属性值或方法实现.如果可能的话,你想要保护你的类的属性/方法,即使是从子类(听起来很讽刺,我知道),然后将其保密.否则让它受到保护.

免责声明:我在Java中没有多少程序:)

  • 澄清一下:Java中的`final class`是一个不能继承的类."最终方法"是非虚拟的,即不能被子类覆盖(Java方法默认是虚拟的).`final field/parameter/variable`是一个不可变的变量引用(在初始化之后它不能被修改). (2认同)

Alo*_*ave 7

你什么时候使用private,何时使用protected

私有继承可以被认为是根据关系而不是IS-A关系实现的.简单地说,继承类的外部接口与继承类没有(可见)关系,它private仅使用继承来实现Base类提供的类似功能.

与Private Inheritance不同,受保护的继承是一种受限制的继承形式,其中派生类IS-A类是Base类,它希望将派生成员的访问权限仅限于派生类.

  • “根据关系实施”是HAS-A关系。如果所有属性都是私有的,则访问它们的唯一方法是通过方法。这就像子类包含超类的对象一样。从具体类中删除所有受保护的属性也意味着也要消除它们的所有继承。 (2认同)
  • 有趣的思考过程 - **私人继承**。@shawnhcorey 感谢您明确指出它指向 *HAS-A* 关系又名关联(可以是聚合或组合)。我觉得这篇文章也应该更新以澄清相同的内容。 (2认同)