对字段和方法使用private而不是protected的原因

时间:2011-02-06 11:36:52

标签: oop visibility private protected

这是一个相当基本的OO问题,但是一段时间以来一直困扰着我。

我倾向于避免在我的字段和方法中使用“私有”可见性修饰符,以支持protected

这是因为,除非我想为我的类的扩展(即在框架中)设置特定的指导原则,否则我认为在基类和子类之间隐藏实现没有任何用处。对于大多数情况,我认为试图限制我的课程将由我或其他用户进行扩展是没有用的。

但是,对于大多数人来说,private修饰符通常是定义非公共字段/方法时的默认选择。

那么,您可以列出private的用例吗?总是使用私有的主要原因是什么?或者你也认为它被过度使用了?

7 个答案:

答案 0 :(得分:30)

有一些共识认为,在OOP中应该prefer composition over inheritance。这有几个原因(谷歌,如果你感兴趣),但主要部分是:

  • 继承很少是最好的工具,并不像其他解决方案那样灵活
  • 受保护的成员/字段构成了您的子类的接口
  • 接口(以及关于它们未来使用的假设)很难正确理解

因此,如果你选择让你的课程继承,你应该有意识地这样做,并考虑到所有的优点和缺点。

因此,最好不要让类继承,而是通过其他方式确保它尽可能灵活(而不是更多)。

这在大型框架中非常明显,在这些框架中,您的课程的使用超出了您的控制范围。对于你自己的小应用程序,你不会注意到这个那么多,但是如果你不小心的话,它(默认继承)迟早会把你咬在后面。

<强>替代

组合意味着您通过显式(完全抽象)接口(虚拟或基于模板)公开可定制性。

因此,不是让Vehicle基类具有虚拟drive()函数(以及其他所有内容,例如价格整数等),而是让Vehicle类获取Motor接口对象,并且Motor接口仅暴露drive()函数。现在,您可以在任何地方添加和重复使用任何类型的电机(或多或少。:)。

答案 1 :(得分:16)

有两种情况,一个成员是protected还是private是否重要:

  1. 如果派生类可以从使用成员中受益,那么使成员“受保护”将允许它这样做,而使其成为“私有”会否认它有益。
  2. 如果基类的未来版本可以通过不使成员在当前版本中运行而受益,那么使成员“private”将允许未来版本更改行为(或消除完全成员),同时使它成为“受保护的”将要求该类的所有未来版本保持相同的行为,从而否定它们可以通过更改它获得的好处。

如果可以想象一个真实的场景,派生类可能会因为能够访问该成员而受益,并且无法想象基类可能会从更改其行为中受益的场景,那么该成员应该是protected [当然,假设它不应该公开]。如果无法想象一个派生类可以从直接访问成员中获得很多好处的场景,但是可以想象通过更改它可以使基类的未来版本受益的场景,那么它应该是private。这些案件非常明确和直截了当。

如果没有任何合理的场景,基类会从更改成员中受益,我建议人们应该倾向于使其成为protected。有人会说“YAGNI”(你不需要它)原则支持private,但我不同意。如果你期望别人继承这个班级,那么让一个成员私有不会假设“YAGNI”,而是“HAGNI”(他不会需要它)。除非“你”需要在课程的未来版本中更改项目的行为,否则“你”不需要它private。相比之下,在许多情况下,您无法预测班级的消费者可能需要什么。这并不意味着,如果成员protected没有首先尝试确定可能会因改变它们而受益的方式,那么YAGNI不会真正适用于这两种决策。 YAGNI适用于遇到未来需求的情况,因此现在无需处理。将一个成员提供给其他程序员privateprotected的决定意味着决定将来会提供哪种类型的潜在需求,并且难以提供另一个。

有时这两种情况都是合理的,在这种情况下,提供两个类可能会有所帮助 - 其中一个类暴露了有问题的成员,而一个类来自那个没有的类(没有标准的惯用语用于派生类)隐藏从其父级继承的成员,虽然声明具有相同名称但没有可编译功能且标有Obsolete属性的新成员会产生这种效果)。作为所涉及权衡的一个例子,考虑List<T>。如果类型将支持数组公开为受保护成员,则可以定义派生类型CompareExchangeableList<T> where T:Class,其中包含将返回T CompareExchangeItem(index, T T newValue, T oldvalue)的成员Interlocked.CompareExchange(_backingArray[index], newValue, oldValue);任何期望List<T>的代码都可以使用这样的类型,但是知道该实例的代码是CompareExchangeableList<T>可以在其上使用CompareExchangeItem。不幸的是,因为List<T>没有将后备数组暴露给派生类,所以不可能定义一个允许CompareExchange列表项的类型,但仍然可以使用期望List<T>的代码

但是,这并不意味着暴露支持阵列完全没有成本;尽管List<T>的所有现有实现都使用单个支持数组,但是当列表超过84K时,Microsoft可能会实现未来版本以使用多个数组,以避免与大对象堆相关的低效率。如果支持数组作为受保护成员公开,则在不破坏任何依赖该成员的代码的情况下实现此类更改是不可能的。

实际上,理想的可能是通过提供受保护的成员来平衡这些兴趣,在给定列表项索引的情况下,该成员将返回包含指示项的数组段。如果只有一个数组,则该方法将始终返回对该数组的引用,其偏移量为零,起始下标为零,长度等于列表长度。如果List<T>的未来版本将数组拆分成多个部分,则该方法可以允许派生类以不具有此类访问权限的方式有效地访问数组的片段[例如,使用Array.Copy]但List<T>可以改变管理其后备存储的方式,而不会破坏正确编写的派生类。如果基本实现发生更改,则编写不正确的派生类可能会被破坏,但这是派生类的错误,而不是基类。

答案 2 :(得分:4)

在默认情况下,我更喜欢私有而不是受保护,因为我遵循原则隐藏尽可能多的可能性,这就是为什么将可见性设置得尽可能低。

答案 3 :(得分:2)

我到达这里。但是,我认为应该有意识地使用受保护的成员变量,因为您不仅计划继承,而且因为有一个可靠的原因派生类不应该使用在基类上定义的Property Setters / Getters。

在OOP中,我们“封装”成员字段,以便我们可以练习控制代表访问和更改它们的属性的方式。当我们在我们的基础上为成员变量定义一个getter / setter时,我们基本上说这是我希望引用/使用这个变量的方式。

虽然存在设计驱动的异常,其中可能需要改变在基类getter / setter方法中创建的行为,但在我看来,这将是在仔细考虑替代方案后做出的决定。

例如,当我发现自己需要直接从派生类访问成员字段时,而不是通过getter / setter,我开始考虑可能应该将特定属性定义为抽象,或者甚至移动到派生类。这取决于层次结构的宽度以及任何数量的其他考虑因素。但对我来说,踩着基类定义的公共财产开始嗅到。

当然,在许多情况下,它“无关紧要”,因为除了访问变量之外,我们没有在getter / setter中实现任何内容。但同样,如果是这种情况,派生类可以通过getter / setter轻松访问。如果一致地使用,这也可以防止以后难以发现的错误。如果基类上的成员字段的getter / setter的behgavior以某种方式更改,并且派生类直接引用Protected字段,则可能会出现问题。

答案 4 :(得分:1)

你走在正确的轨道上。您将某些内容设为私有,因为您的实现依赖于它不会被用户或后代更改。

我默认为私有,然后有意识地决定我将要揭示的内部工作是否以及有多少,你似乎在基础上工作,无论如何它都会暴露,所以继续它。只要我们都记得穿过所有的眼睛并点上所有发球台,我们就会很好。

另一种看待它的方法就是这样。 如果您将其设为私有,则某些人可能无法按照您的实现执行他们想要的操作。

如果您不将其设为私有,则可能有人可能会做一些您不希望他们与您的实施相关的事情。

答案 5 :(得分:1)

我从1993年的C ++和1995年的Java开始编写OOP。我一次又一次地看到需要扩充或修改类,通常会增加与类紧密集成的额外功能。 OOP方法是对基类进行子类化并在子类中进行更改。例如,某些其他操作需要最初仅在基类中的其他地方引用的基类字段,或者某些其他活动必须更改字段的值(或字段的包含成员之一)。如果该字段在基类中是私有的,则子类无法访问它,无法扩展该功能。如果该字段受到保护,则可以这样做。

子类与基类具有特殊关系,类层次结构中其他类的其他类不具有这些关系:它们继承基类成员。继承的目的是访问基类成员;私人的thwart继承。基类开发人员如何知道没有子类需要访问成员?在某些情况下,这可以是明确的,但私人应该是例外而不是规则。子类化基类的开发人员具有基类源代码,因此他们的替代方案是直接修改基类(可能只是在子类化之前将私有状态更改为protected)。这不是很干净,很好的做法,但这就是私人让你做的事情。

答案 6 :(得分:0)

我是OOP的初学者,但自从ACM和IEEE的第一篇文章以来就一直存在。从我记忆中来看,这种开发方式更多的是用于建模。在现实世界中,包括流程和操作在内的事物将具有“私有,受保护和公共”元素。所以要真实对象.....

除了建模之外,编程更多的是解决问题。当涉及到制定可靠的解决方案时,“私人,受保护和公共”元素的问题只是一个问题。作为一个解决问题的人,我不会错误地认识到其他人如何使用我的解决方案来解决他们自己的问题。现在请记住,发出....的一个主要原因是允许一个地方进行数据检查(即,在对象中使用它之前验证数据是否在有效范围和结构中)。

考虑到这一点,如果您的代码解决了它的设计问题,那么您已经完成了自己的工作。如果其他人需要您的解决方案来解决相同或类似问题 - 那么,您真的需要控制他们如何做到这一点。我会说,“只有你获得了一些好处,或者你知道你设计中的弱点,所以你需要保护一些东西。”