关于OOP:私有与受保护-可见性良好实践问题

Private vs Protected - Visibility Good-Practice Concern

我一直在寻找,我知道理论上的不同。

  • 公共-任何类/函数都可以访问方法/属性。
  • 受保护-只有此类和任何子类可以访问方法/属性。
  • private-只有此类可以访问方法/属性。它甚至不会被继承。

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

到目前为止,为了保留继承和多态性的概念,我使用public来处理任何应该从外部访问的内容(如构造函数和主类功能),使用protected来处理内部方法(逻辑、辅助方法等)。我走对了吗?

(请注意,这个问题是为我准备的,但也供以后参考,因为我没有见过这样的问题)。


不,你走错了。一个很好的经验法则是:尽可能地让一切都变得私人化。这使得类更加封装,并允许在不影响使用类的代码的情况下更改类的内部。

如果将类设计为可继承的,那么仔细选择可能被子类重写和可访问的内容,并使其受到保护(并且,如果您想使其成为可访问的但不可重写的话,则要讲Java)。但是请注意,一旦您接受了类的子类,并且有一个受保护的字段或方法,那么这个字段或方法就是类的公共API的一部分,以后在不破坏子类的情况下不能更改。

不打算继承的类应该是最终的(在Java中)。为了进行单元测试,您可以放宽一些访问规则(私有到受保护,最终到非最终),但随后对其进行文档记录,并明确表示尽管该方法受保护,但不应该重写它。


首先,我要说的是,我在这里主要讨论方法访问,在稍微小一点的范围内,将类标记为最终的,而不是成员访问。

旧智慧

"mark it private unless you have a good reason not to"

在开发人员库空间和VCS/Dependency Management还未被开源所统治之前,这在编写它的日子里是有意义的。由于Github、Maven等原因,它变得高度协作。那时,通过限制图书馆的使用方式,也可以赚钱。我在职业生涯的头8到9年里,可能都严格遵守了这个"最佳实践"。

今天,我认为这是个坏建议。有时有合理的理由将方法标记为private,或类final,但这是非常罕见的,即使这样,它也可能不会改进任何东西。

你曾经有过:

  • 对一个库等感到失望、惊讶或伤害,因为它有一个可以通过继承和很少几行代码修复的bug,但是由于私有/最终方法和类被迫等待一个可能永远不会出现的官方补丁?我有。
  • 想要将库用于与作者设想的稍有不同的用例,但由于私有/最终方法和类而无法这样做?我有。
  • 对一个在可扩展性方面过于宽松的库等感到失望、惊讶或伤害?我没有。

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

合理化1:这是不安全的,没有理由重写特定的方法

对于是否需要重写我编写的特定方法,我无法计算出我出错的次数。在研究了几个流行的开源libs之后,我学到了将事物标记为私有的真正代价。它经常消除不可预见的问题或用例的唯一实际解决方案。相反,在16年多的专业发展中,我从来没有因为与API安全相关的原因而后悔将方法标记为受保护而不是私有。当开发人员选择扩展一个类并重写一个方法时,他们会有意识地说"我知道我在做什么",并且为了提高工作效率,这应该足够了。时期。如果这很危险,请在类/方法javadocs中注意,不要盲目地关上门。

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

合理化2:保持公共API/JavaDocs的清洁

这一点更为合理,根据目标受众的不同,它甚至可能是正确的做法,但值得考虑的是保持API"干净"的实际成本是:可扩展性。出于上述原因,为了以防万一,标记默认保护的对象可能更有意义。

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

这也是合理的,但作为一个消费者,我每次都会选择限制性较小的竞争对手(假设没有明显的质量差异)。

永不言败

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

这个建议最适合那些在图书馆或更大规模的项目上工作的人,这些项目已经被分解成模块。对于更小或更大的单块项目来说,这并没有那么重要,因为不管怎样,您都可以控制所有代码,并且在需要时可以很容易地更改代码的访问级别。尽管如此,我还是会给出同样的建议:—)


我刚才读了一篇文章,说要尽可能地把每门课都锁起来。使所有内容最终化和私有化,除非您立即需要向外部世界公开某些数据或功能。扩展范围是很容易的,以便在以后更为允许,但不是反过来。首先考虑做尽可能多的东西来选择final,这将使在privateprotected之间的选择更加容易。

  • 使所有类成为最终类,除非您需要立即对它们进行子类化。
  • 使所有方法成为最终方法,除非您需要子类化并立即重写它们。
  • 使所有方法参数都成为最终参数,除非需要在方法体中更改它们,这在大多数情况下都有点尴尬。
  • 现在,如果你只剩下最后一节课,那就把所有的事情都变成私人的,除非世界上绝对需要的东西——把它公之于众。

    如果您只剩下一个具有子类的类,那么请仔细检查每个属性和方法。首先考虑是否要将该属性/方法公开到子类。如果这样做,那么考虑如果子类在重写过程中破坏了属性值或方法实现,它是否会对对象造成破坏。如果可能的话,并且您想要保护类的属性/方法,甚至不受子类的影响(听起来很讽刺,我知道),那么就将其设为私有的。否则要保护它。

    免责声明:我在爪哇的计划不多:


    停止滥用私人领域!!!!

    这里的评论似乎压倒性地支持使用私有领域。好吧,那我有别的话要说。

    私人领域原则上是好的吗?对。但是说一个黄金法则就是在你不确定的时候把所有的事情都保密,这绝对是错误的!在遇到问题之前,你是看不到问题的。在我看来,如果您不确定,您应该将字段标记为受保护。

    有两种情况需要扩展类:

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

    在第一种情况下,私有字段没有问题。人们滥用私人领域的事实让你发现自己无法修改狗屎时很沮丧。

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class Car {
        private screw;
        public assembleCar() {
           screw.install();
        };
        private putScrewsTogether() {
           ...
        };
    }

    图书馆作者认为:我的图书馆用户没有理由访问assembleCar()的实现细节,对吗?让我们把螺丝钉标为私密的。

    嗯,作者错了。如果您只想修改assembleCar()方法,而不想将整个类复制到包中,那么您就走运了。您必须重写自己的screw字段。假设这辆车使用了十几个螺丝,每一个螺丝都涉及一些不同私有方法的非私有初始化代码,这些螺丝都标记为私有。在这一点上,它开始吸。

    是的,你可以和我争论,图书馆作者本可以写更好的代码,这样私有字段就没有问题了。我不是说私人领域是OOP的问题。当人们使用它们时是一个问题。

    这个故事的寓意是,如果你在写一个图书馆,你永远不知道你的用户是否想访问某个特定的领域。如果你不确定的话,把它标记为1(3),这样每个人以后都会更快乐。至少不要滥用私人领域。

    我非常支持尼克的回答。


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

    私有继承可以从关系的角度来考虑,而不是从IS-A关系的角度来考虑。简单地说,继承类的外部接口与继承类没有(可见)关系,它只使用private继承来实现基类提供的类似功能。

    与私有继承不同,受保护继承是一种受限制的继承形式,其中派生类是一种基类,它希望将派生成员的访问仅限于派生类。


    好吧,这都是关于封装的,如果PayBill类处理付款的账单,那么在产品类中,为什么它需要整个账单流程,即付款方式,如何支付到哪里付款。因此,对于那些其他类也会使用的类和对象,只允许将用于其他类和对象的内容放在公共内容之外,仅对扩展类的限制进行保护。因为你是内海夫人,私人学校就像是江户一号(6号),你可以看到它(你只上一个班)。