ChatGPT解决这个技术问题 Extra ChatGPT

私人与受保护 - 可见性良好实践问题[关闭]

关闭。这个问题是基于意见的。它目前不接受答案。想改进这个问题?更新问题,以便可以通过编辑这篇文章用事实和引用来回答它。 4年前关闭。改进这个问题

我一直在寻找,我知道理论上的区别。

public - 任何类/函数都可以访问方法/属性。

protected - 只有这个类和任何子类可以访问方法/属性。

private - 只有这个类可以访问方法/属性。它甚至不会被继承。

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

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

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

有关系吗?任何支持 OOP 的语言都有这个问题。我碰巧用 PHP 编程,但我认为这个问题适用于任何 OOP 支持语言。
好吧,很公平,只是想知道您是否忘记标记。现在我看到了 oop 标签。
我必须为类的每个属性设置可见性(私有/公共/受保护)?还是只有其中一些需要具有可见性类型?如果是,如何决定哪些属性需要在类中设置可见性?
顺便说一下,受保护和私有之间的区别似乎很明显。如果子类将使用方法/变量,则使用 protected,否则使用 private。具体来说,如果子类必须在父类中重新定义一个非常相似的私有变量,只需将其设为受保护。
C# 语言中还有另一个访问说明符 internal,它限制物理程序集中的类或其成员的访问级别。不过,我不确定它是否支持或其他语言中的类似内容。

J
JB Nizet

不,你没有走在正确的轨道上。一个好的经验法则是:让一切尽可能私密。这使您的类更加封装,并允许更改类的内部而不影响使用您的类的代码。

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

一个不打算被继承的类应该是最终的(在 Java 中)。为了进行单元测试,您可能会放宽一些访问规则(私有到受保护,最终到非最终),然后记录它,并明确说明虽然该方法受到保护,但它不应该被覆盖。


这里真正的问题是关于 privateprotected 我什么时候想要继承属性,什么时候不想要?我真的不知道将来有时用户是否想参加我的课程并扩展它......
好吧,问题不是用户想要覆盖什么,问题是你想要允许覆盖什么。通常它有助于换边并尝试思考:如果我使用该类并创建了一个子类,我希望能够覆盖什么?有时其他用户仍然会错过一些东西......
受保护的字段在继承中是不好的做法!。请注意它。 Here 是为什么
私有字段在继承实践中很糟糕! (过分夸大)请阅读我的回答。
典型的控制狂意见。
C
Community

让我先说我在这里主要谈论方法访问,并在稍微较小的程度上将类标记为final,而不是成员访问。

古老的智慧

“将其标记为私人,除非你有充分的理由不这样做”

在编写它的日子里,在开源主导开发者库空间和 VCS/依赖管理之前,它是有意义的。多亏了 Github、Maven 等,它变得高度协作。当时还可以通过限制使用库的方式来赚钱。在我职业生涯的前 8 年或 9 年中,我可能严格遵守这种“最佳实践”。

今天,我认为这是一个糟糕的建议。有时将方法标记为私有或类 final 有合理的理由,但这种情况非常罕见,即使这样也可能没有任何改进。

你有没有:

对一个库等有一个可以通过继承和几行代码修复的错误,但由于私有/最终方法和类被迫等待可能永远不会出现的官方补丁而感到失望、惊讶或伤害?我有。

想要将库用于与作者想象的略有不同的用例,但由于私有/最终方法和类而无法这样做?我有。

对可扩展性过于宽松的库等感到失望、惊讶或伤害?我没有。

这是我听说过的将方法默认标记为私有的三个最大的合理化:

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

对于是否需要覆盖我编写的特定方法,我数不清有多少次我错了。在研究了几个流行的开源库之后,我艰难地了解到将事物标记为私有的真正成本。它通常会消除无法预见的问题或用例的唯一实用解决方案。相反,在 16 年多的专业发展中,我从来没有因为与 API 安全相关的原因而后悔将方法标记为受保护而不是私有。当开发人员选择扩展一个类并覆盖一个方法时,他们有意识地说“我知道我在做什么”。为了提高生产力,这应该足够了。时期。如果它很危险,请在类/方法 Javadocs 中注明,不要只是盲目地关上门。

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

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

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

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

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

永远不要把话说绝了

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

该建议最适合那些从事图书馆或已分解为模块的大型项目的人。对于更小或更单一的项目,这并不重要,因为无论如何您都可以控制所有代码,并且在需要时可以轻松更改代码的访问级别。即使那样,我仍然会给出相同的建议:-)


Wrt 您的 Rationalization # 1 - 当我将某些东西标记为受保护时,我选择明确地这样做,因为我觉得这种行为不是最终的,并且可能是我的子类想要覆盖它的情况。如果我不太确定,那么我想默认将其设为私有。特别是在像 C# 这样默认保持方法非虚拟的语言中,仅添加受保护的访问说明符是没有意义的。您必须同时添加 virtualprotected 关键字以使您的意图非常清晰。此外,人们更喜欢关联而不是继承,因此 protected 作为默认值很难感知
使方法可覆盖所需的关键字组合是恕我直言的语言细节。我提出的合理化#1的反驳论点直接针对您提到“如果我不太确定......”的情况。实际上,如果您想不出它危险的原因,那么通过选择可扩展性可以获得更多收益。当您说“关联”时,我将其视为组合的同义词,在这种情况下,我认为这两种方式都不重要。
我不记得有多少次我不得不“克隆”底层类只是因为我想覆盖 1 或 2 或方法。就像您所说的那样,随着我们共享比以往任何时候都多的代码的社会趋势,默认情况下使用受保护比私有更有意义。即对自己的逻辑更加开放。
同意。我只是想调整 Java 的 BufferedReader 来处理自定义行分隔符。由于私有字段,我必须克隆整个类,而不是简单地扩展它并覆盖 readLine()。
L
Larry

停止滥用私有领域!!!

这里的评论似乎压倒性地支持使用私有字段。好吧,那我有不同的话要说。

原则上私有领域好吗?是的。但是说黄金法则是在不确定时将所有内容保密绝对是错误的!在遇到问题之前,您不会看到问题。我认为,如果您不确定,您应该将字段标记为受保护。

有两种情况要扩展一个类:

您想向基类添加额外的功能

您想修改当前包之外的现有类(可能在某些库中)

在第一种情况下,私有字段没有任何问题。人们滥用私有字段的事实让您在发现自己无法修改狗屎时感到非常沮丧。

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

class Car {
    private screw;
    public assembleCar() {
       screw.install();
    };
    private putScrewsTogether() {
       ...
    };
}

库作者认为:我的库的用户没有理由需要访问 assembleCar() 的实现细节,对吧?让我们将螺丝标记为私有。

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

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

这个故事的寓意是,如果你正在编写一个库,你永远不知道你的用户是否想要访问一个特定的领域。如果您不确定,请将其标记为 protected,这样以后每个人都会更开心。 至少不要滥用私有字段

我非常支持尼克的回答。


使用私有字段主要是说继承是错误的抽象来改变类/对象的某些行为(如果有意识地使用)。更改通常会默默地违反 LSP,因为在 API 没有更改的情况下更改了行为。很好的答案,+1。
传道!在尝试编写 Minecraft 模组时,私人是我存在的祸根。没有什么比不得不使用反射或 ASM 来解决这个问题更烦人的了。
据我了解尼克的回答,他主要指的是方法而不是属性。我完全同意我们不应该每次都将方法声明为私有,并且这些方法的使用需要深思熟虑,但是 cmon 为什么你会建议将属性声明为私有,只是因为你想更容易地“重写”类。当我每天与 3rd p 一起工作时,完全分享您的挫败感,但让我们鼓励人们创建可靠的架构,而不仅仅是说“代码最终会变得很糟糕,所以让我们从一开始就保护好,这样我们就可以轻松地重写它”。虽然我和你一样沮丧。
A
Anurag

不久前我读了一篇文章,其中谈到尽可能锁定每个班级。除非您有立即需要向外界公开某些数据或功能,否则请确保所有内容都是最终的和私有的。以后将范围扩大到更允许的范围总是很容易的,但反之则不然。首先考虑尽可能多地制作 final,这将使在 privateprotected 之间进行选择变得更加容易。

除非您需要立即对它们进行子类化,否则将所有类设为最终类。除非您需要子类化并立即覆盖它们,否则将所有方法设为最终方法。将所有方法参数设为最终参数,除非您需要在方法体中更改它们,这在大多数情况下还是有点尴尬。

现在,如果您还剩下最后一堂课,那么将所有内容都设为私有,除非世界绝对需要某些东西-将其公开。

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

免责声明:我不会在 Java 中编写太多程序 :)


澄清一下:Java 中的 final class 是一个不能被继承的类。 final method 是非虚拟的,即不能被子类覆盖(Java 方法默认是虚拟的)。 final field/parameter/variable 是一个不可变的变量引用(从某种意义上说,它在初始化后就无法修改)。
有趣的是,我看到了完全相反的建议,除非确定覆盖所讨论的事物有可能破坏不变量,否则任何事情都不应该是最终的。这样,任何人都可以根据需要自由扩展您的课程。
您能否列举一个在您的编程生涯中将方法参数标记为“final”对您的理解产生影响的案例? (除非编译器要求)
A
Alok Save

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

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

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


“在关系方面实施”是一种HAS-A关系。如果所有属性都是私有的,那么访问它们的唯一方法是通过方法。这就像子类包含超类的对象一样。从具体类中消除所有受保护的属性也意味着消除它们的所有继承。
有趣的思考过程——私人继承。 @shawnhcorey 感谢您明确指出它指向 HAS-A 关系,即关联(可以是聚合或组合)。我觉得这篇文章也应该更新以澄清这一点。
u
ujwal dhakal

好吧,如果paybill类处理支付账单,那么这就是封装,那么在产品类中,为什么它需要整个计费过程,即支付方式如何支付在哪里支付..所以只让其他类和对象使用什么对于其他类也将使用的那些公共,仅此而已,仅针对扩展类的限制进行保护。因为你是 madara uchiha,所以私人就像 "limboo" 你可以看到它(你只上课)。