何时编写私有方法,而不是受保护的方法?

41

如果我正在编写一个类,那么我何时应该将方法设置为私有(private),而不是受保护的(protected)?换句话说,我如何事先确定客户端程序员永远不需要重写一个方法?在涉及外部考虑因素(如数据库连接)的情况下呢?


2
这个应该标记为php吗?看起来似乎没有什么区别,但是我对PHP中的面向对象编程不太熟悉,所以私有(private)和受保护(protected)在PHP中可能与其他面向对象语言完全不同。 - Sarah Vessels
1
我对PHP以外的面向对象编程不太熟悉,所以我不想做任何假设。也许它确实非常不同,我不知道! :) - user151841
8个回答

55

publicprotected方法构成了你的对象的“接口”,public为使用(委托)你的类的开发人员提供,而protected则为希望通过子类化来扩展你的对象功能的开发人员提供。

请注意,即使你的类将被子类化,也不必提供protected方法。

需要仔细考虑publicprotected接口,特别是如果这是一个由你控制之外的开发人员使用的API,因为对接口的更改可能会破坏那些假设现有接口如何工作的程序。

private方法完全是为了对象作者而存在的,可以任意重构、更改和删除。

我会默认选择private,如果你发现需要暴露更多方法,请仔细考虑它们将如何被使用——特别是如果它们是虚函数——如果它们被另一个开发人员完全替换为任意的替代函数,你的类还能正常工作吗?然后设计一些适当的protected方法,这些方法对于开发人员子类化你的对象非常有用(如果需要),而不是暴露现有函数。


5
+1的重点是,当你把私有内容变成受保护的时候,你必须用另一种方式来考虑你的实现方式。你不能简单地把所有东西都设为受保护或公开状态。 - tanascius
/-- +1 把我想说的话都说出来了。 - Grant Palin
很好的解释,但默认使用“private”是我停止使用某些类的原因。我无法扩展它们,也不愿意复制粘贴它们。 - romaninsh
惊人的解释。 - Yugal Jindle

28
换句话说,我如何知道客户端程序员永远不需要覆盖方法?你无法预测。而且你也不需要。你的工作不是去预测开发人员是否会想要覆盖一个方法,更别提如何覆盖了。只需假设他想这样做,并使他能够在不触及你的代码的情况下这样做。因此,如果没有必要,请不要将方法声明为私有的。
如果开发人员感觉需要调整类的某些功能,他可以从许多结构和行为模式中选择一种来实现,例如装饰器、适配器或子类化。使用这些模式是好的,因为它将更改封装到开发人员自己的类中,留下你自己的代码不受影响。通过将方法声明为私有的,你确保开发人员将对你的类进行修改。这是不好的。
一个完美的例子是Zend Framework的DB适配器。他们不鼓励使用持久连接,他们的适配器也不提供这种方式。但是,如果你仍然想要这个,而适配器方法被标记为私有的(它并不是,但如果是呢?),你将不得不直接在它的类中更改适配器代码,或者复制并粘贴代码到自己的适配器类中,从而有效地复制99%的类来更改单个函数调用。每当这个适配器有更新时,你要么会失去你的更改,要么你将无法获取它(如果你选择复制和粘贴)。如果它被标记为protected(正如它现在所标记的那样),你只需编写一个pConnectAdapter子类即可。
此外,在子类化时,你实际上是表明子类“是一个”父类。因此,你可以期望派生类具有与父类相同的功能。如果父类中有不应该在子类中可用的功能,则禁用它在概念上属于子类。这就是为什么我认为将所有方法和属性默认设置为protected比较好的实践,只有那些允许其他类或脚本与我的类交互的方法(不包括属性)才标记为public,但只有少数私有的东西。这样一来,我给开发人员选择使用我的类的方式以及调整它的选项。如果他在这个过程中出了问题,很可能是他的错,而不是我的。
更新:自从我四年前写这篇文章以来,我得出了一个结论,即默认将某些东西设置为protected而不是private经常会导致子类次优。这是因为人们会开始使用你提供的protected内容。这反过来意味着你必须将所有这些方法视为API,并且不能随意更改它们。因此,最好仔细考虑要提供哪些扩展点并保持其他所有内容私有。参见http://fabien.potencier.org/article/47/pragmatism-over-theory-protected-vs-private获取类似观点。

因此,如果不必要,请勿声明方法为私有。好吧,我从来没有必须这样做 - 我总是选择这样做。该类本身将正常工作,无论方法是“private”还是“protected”。因此,“必须”仅在我预期未来客户程序员的需求时才会出现。除非我想确保父类的某些纯内部功能永远不会被客户程序员破坏 - 这就是你的意思吗?除非是这种情况,否则使用“protected”? - user151841
1
是的,我想是这样。我非常清楚有很多人喜欢将私有作为默认选项,并在需要时仅暴露所需内容。但是考虑到未来的开发人员,您无法预测他们需要什么。当然可以自行决定采用不同的方式。几个月前也有一个类似的问题,您可能也想阅读一下:https://dev59.com/HnRC5IYBdhLWcg3wD87r - Gordon
9
在我从事大型PHP项目的工作时间越长,我越认同这一观点。往往情况下,我希望一个类已经将某个方法声明为protected而不是private,这会迫使我编写一个补丁程序。作为一个程序员,如果我重写了受保护的方法,而没有理解它的作用,我并不指望我继承的类能够完美地工作。相反,我会阅读代码,理解正在做什么以及如何完成,然后相应地重写该方法。我期望与我的代码一起工作的人也能这样做。+1 - Christof

10

我通常会从最低层级开始。如果你不确定,就将其设为私有。然后根据需要,可以将其改为受保护的或公开的。

这样做的想法是,从私有到受保护并不会造成破坏性变化,但从受保护到私有可能会造成破坏性变化。


这是一个好想法,但封装呢?如果我正在编写一个子类并且需要在父类中使用私有方法,那么我就会在父类的代码中瞎搞了。 - user151841
没错。很多事情取决于您的应用程序的具体情况。您将设置规则,以确定哪些内容可以/不可以被子类覆盖或调用。有些部分对功能非常核心,您需要将它们设置为私有,以保护未来的开发人员不会意外地覆盖或调用它们。其他部分是可选的或可配置的,应根据情况公开为受保护的或公共的。 - brendan

2
不要把private/protected/public的问题看作程序员是否“需要”一个方法。可以将其看作你是否想允许他们访问它。
如果认为他们需要更改数据库连接字符串,则将其设置为public。

那么默认应该是受保护的,只有当我确信它们永远不应该被访问时才设置为私有的吗? - user151841
如果必须选择的话,我会按照 Brendan 的答案选择 "private" 作为默认值,但是我并不认为应该有一个所谓的 "默认值"。你真的需要考虑这个方法具体做什么,然后再决定它应该是什么。 - Robin Day

2

我通常将所有方法默认设置为private。这样做是为了保持接口干净易于维护。

相比之下,更改或隐藏已经可见的方法比使私有方法更加可见要困难得多。至少如果你需要与现有客户端代码兼容的话。


0
换句话说,我如何预先知道客户端程序员永远不需要覆盖方法?
如果你不知道,就假设他们需要。如果这对你来说没问题(即,如果你认为他们应该能够),那么使用“protected”;否则使用“private”。

我想我的问题是,我怎么知道呢?我觉得问自己这个问题就像占卜未来一样。我怎么可能知道未来某个程序员的需求呢?看起来你告诉我的是始终使用protected,因为我还没有遇到过一个情况,我知道客户程序员永远不需要它。 - user151841
你不能“知道”,你只能根据你对应用程序和将使用/消费它的开发人员的了解做出有根据的推测。 - brendan
3
我不同意。最好假设他们不需要覆盖它。在明确之前保持私密! - Martin Wickman
您是否有将某些功能暴露在系统外的要求? - Kwebble

0
私有成员用于封装类的内部工作。使用它们来保存只有您想访问的数据。例如,假设您有一个名为_name的字段和一个名为GetName()/SetName(name)的getter/setter。也许您希望在允许SetName成功之前对名称进行一些语法检查,否则会抛出异常。通过将_name设置为私有,您确保在对名称进行任何更改之前将进行此语法检查(除非您在自己的类中,在自己的代码中更改_name)。如果将其设置为受保护,则表示对于您类的任何潜在未来继承者,“请随意修改我的字段”。
通常情况下,受保护的使用很少,仅在特殊情况下使用。例如,您可能具有受保护的构造函数,该构造函数向子类公开了一些附加的构造功能。

0

我通常会将所有东西都声明为private,并在需要从基类调用时重构代码。

除非我感到懒惰,将所有可能不安全的内容都声明为protected


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接