为什么方法和属性的可见性很重要?
为什么不应该让所有方法和属性都可以从任何地方(即 public
)访问?
您能否给我一个示例,说明如果我将属性声明为 public
可能会遇到的问题?
Why shouldn't one leave all methods and attributes accessible from anywhere (i.e. public
)?
Can you give me an example of a problem I can run into if I declared an attribute as public
?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(12)
将麦当劳视为一个对象。有一个众所周知的公共方法来订购巨无霸。
在内部,将会有无数次其他调用来实际获取制作巨无霸的材料。他们不想让您知道他们的供应链是如何运作的,因此您得到的只是公共
Gimme_a_BigMac()
调用,并且永远不会允许您访问Slaughter_a_cow()
或Buy_potatoes_for_fries()
方法。对于您自己的代码,没有人会看到,请继续将所有内容公开。但是,如果您正在创建一个可供其他人重用的库,那么您就需要保护内部细节。这使得麦当劳可以自由地改用斯科蒂在肉饼上微笑,而不必打电话给卡车运输公司通过陆路运送肉。最终用户永远不会知道其中的区别 - 他们只是得到了巨无霸。但在内部,一切都可能发生根本性的改变。
Think of McDonald's as an object. There's a well known public method to order a BigMac.
Internally there's going to be a few zillion other calls to actually GET the materials for making that Bigmac. They don't want you to know how their supply chain works, so all you get is the public
Gimme_a_BigMac()
call, and would never ever allow you to get access to theSlaughter_a_cow()
orBuy_potatoes_for_fries()
methods.For your own code, that no one will ever see, go ahead and leave everything public. But if you're doing a library for others to reuse, then you go and protect the internal details. That leaves McDonald's free to switch to having Scotty beam over a patty rather than having to call up a Trucking company to deliver the meat by land. The end-user never knows the difference - they just get their BigMac. But internally everything could fundamentally change.
因为那太贵了。
我制定的每一个公共方法都必须经过架构师团队的精心设计,然后批准,并且必须实施才能 >面对任意敌对或有缺陷的调用者时具有鲁棒性,它必须经过全面测试,测试期间发现的所有问题都必须添加回归套件,方法必须是记录,该文档必须翻译成至少十二种不同的语言。
不过,最大的成本是:该方法必须保持不变,永远不变,阿门。如果我在下一个版本中决定我不喜欢该方法的功能,我无法更改它,因为客户现在依赖它。破坏公共方法的向后兼容性会给用户带来成本,而我不愿意这样做。公共方法的糟糕设计或实现会给下一版本的设计者、测试者和实现者带来高昂的成本。
公共方法很容易花费数千甚至数万美元。在一个课程中制作一百个这样的课程,那就是一个价值百万美元的课程。
私有方法没有这些成本。 明智地使用股东的钱;尽可能将所有内容设为私有。
Because that is far too expensive.
Every public method that I make has to be carefully designed and then approved by a team of architects, it has to be implemented to be robust in the face of arbitrarily hostile or buggy callers, it has to be fully tested, all problems found during testing have to have regression suites added, the method has to be documented, the documentation has to be translated into at least twelve different languages.
The biggest cost of all though is: the method has to be maintained, unchanged, forever and ever, amen. If I decide in the next version that I didn't like what that method did, I can't change it because customers now rely on it. Breaking backwards compatibility of a public method imposes costs on users and I am loathe to do that. Living with a bad design or implementation of a public method imposes high costs on the designers, testers and implementers of the next version.
A public method can easily cost thousands or even tens of thousands of dollars. Make a hundred of them in a class and that's a million dollar class right there.
Private methods have none of those costs. Spend shareholder money wisely; make everything private that you possibly can.
将可见范围视为信任的内部圈子。
以您自己为例,思考哪些活动是公共的,哪些活动是私人的或受保护的。有很多事情您不能委托任何人代表您做。有些可以很好地触发,有些则访问权限有限。
同样,在编程中,范围为您提供了创建不同信任圈的工具。此外,将事物设为私有/受保护,可以让您更好地控制正在发生的事情。例如,您可以允许第三方插件扩展您的某些代码,但它们的范围可能会受到限制。
因此,总而言之,范围为您提供了额外的安全级别,并使事情比其他情况更有条理。
Think of visibility scopes as inner circles of trust.
Take yourself as an example, and think about what activities are public and what are private or protected. There are number of things that you are not delegating for anybody to do on your behalf. There are some that are fine others to trigger and there are some with limited access.
Similarly, in programming, scopes give you tools for creating different circles of trust. Additionally, making things private/protected, give you more control on what's happening. For example, you can allow 3rd-party plugins that can extend some of your code, while they can be limited to the scope of how far they can go.
So, to generalize, scopes give you the extra level of security and keeps things more organized that they would be otherwise.
因为这违反了 OOP 的一个关键原则——封装的概念。
Because that violates the concept of encapsulation, a key tenet of OOP.
你说这是你冒的风险?
您的代码声明
$bar
应包含SomeObject
的对象实例。但是,任何使用您的代码的人都可以这样做......并且任何依赖 Foo::$bar 作为
SomeObject
的代码都会中断。使用 getter 和 setter 以及受保护的属性,您可以强制执行此期望:现在您可以确定任何时候设置 Foo::$bar 时,它将带有
SomeObject
的对象实例。A risk you run, you say?
Your code states that
$bar
should contain an object instanceofSomeObject
. However, anyone using your code could do... and any code relying on Foo::$bar being a
SomeObject
would break. With getters and setters and protected properties, you can enforce this expectation:Now you can be certain that any time Foo::$bar is set, it will be with an object instanceof
SomeObject
.通过隐藏实现细节,还可以防止对象进入不一致的状态。
这是一个人为设计的堆栈示例(伪代码)。
如果将这两个变量设为私有,则实现可以正常工作。但如果是公共的,你可以通过为 currentStackPosition 设置不正确的值或直接修改 List 来轻松破坏它。
如果您只公开功能,那么您就提供了其他人可以使用和信任的可靠合约。公开实现只是让它成为一个可能有效的事情,没有人会扰乱它。
By hiding implementation details, it is also preventing an object from getting into an inconsistent state.
Here is an contrived example of a stack (pseudo code).
If you make both variables private the implementation works fine. But if public you can easily break it by just setting an incorrect value for currentStackPosition or directly modifying the List.
If you only expose the functions you provide a reliable contract that others can use and trust. Exposing the implementation just make it a thing that might work of nobody messes with it.
任何语言都不需要封装,但它很有用。
封装用于最大程度地减少更改传播概率最高的潜在依赖项数量,还有助于防止不一致:
简单示例:假设我们创建了一个包含四个变量 - 长度、宽度、面积、周长。请注意,面积和周长是从长度和宽度得出的(通常我不会为它们创建变量),因此改变长度会改变面积和周长。
如果您没有使用正确的信息隐藏(封装),那么利用该 Rectangle 类的另一个程序可以在不改变面积的情况下改变长度,并且您将得到不一致的 Rectangle。如果没有封装,可以创建一个长为 1、宽为 3、面积为 32345 的矩形。
使用封装,我们可以创建一个函数,如果程序想要更改矩形的长度,该对象将适当地更新其面积和周长而不会不一致。
封装消除了不一致的可能性,并将保持一致性的责任转移到对象本身而不是使用它的程序上。
然而,同时封装有时是一个坏主意,运动规划和碰撞(在游戏编程中)是特别可能出现这种情况的领域。
问题在于,封装在需要的地方非常棒,但是当应用在不需要的地方时就很糟糕了,比如当有全局属性需要由一组封装来维护时,因为 OOP 强制封装不不管怎样,你都被困住了。例如,对象的许多属性都是非局部的,例如任何类型的全局一致性。 OOP 中经常发生的情况是,每个对象都必须对其全局一致性条件的视图进行编码,并尽自己的一份力量来帮助维护正确的全局属性。如果您确实需要封装以允许替代实现,这可能会很有趣。但如果你不需要它,你最终会在多个地方编写大量非常棘手的代码,这些代码基本上做同样的事情。一切看起来都是封装的,但实际上是完全相互依赖的。
Encapsulation is not needed in any language, but it's useful.
Encapsulation is used to minimise the number of potential dependencies with the highest probability of change propagation also it helps preventing inconsistencies :
Simple example: Assume we made a Rectangle class that contained four variables - length, width, area, perimeter. Please note that area and perimeter are derived from length and width (normally I wouldn't make variables for them), so that changing length would change both area and perimeter.
If you did not use proper information hiding (encapsulation), then another program utilizing that Rectangle class could alter the length without altering the area, and you would have an inconsistent Rectangle. Without encapsulation, it would be possible to create a Rectangle with a length of 1 and a width of 3, and have an area of 32345.
Using encapsulation, we can create a function that, if a program wanted to change the length of the rectangle, that the object would appropriately update its area and perimeter without being inconsistent.
Encapsulation eliminates the possibilities for inconsistency, and shifts the responsibility of staying consistent onto the object itself rather than a program utilizing it.
However at the same time encapsulation is sometimes a bad idea, and motion planning and collision (in game programming) are areas where this is particularly likely to be the case.
the problem is that encapsulation is fantastic in places where it is needed, but it is terrible when applied in places where it isn’t needed like when there are global properties that need to be maintained by a group of encapsulation, Since OOP enforced encapsulation no matter what, you are stuck. For example, there are many properties of objects that are non-local, for example, any kind of global consistency. What tends to happen in OOP is that every object has to encode its view of the global consistency condition, and do its part to help maintain the right global properties. This can be fun if you really need the encapsulation, to allow alternative implementations. But if you don’t need it, you end up writing lots of very tricky code in multiple places that basically does the same thing. Everything seems encapsulated, but is in fact completely interdependent.
好吧,事实上,您可以公开所有内容,并且当您清楚地说明契约是什么、使用对象的正确方法时,它不会破坏封装。也许不是属性,但方法通常比它们必须的更加隐藏。
请记住,并不是您(API 设计者)通过公开事物来破坏封装。类的用户可以通过调用其应用程序中的内部方法来执行此操作。您可以因为他们试图这样做而拍手(即,将方法声明为私有),也可以将责任传递给他们(例如,通过在非 API 方法前加上“_”前缀)。您真的关心有人是否按照您建议的其他方式使用您的库来破坏他的代码吗?我不知道。
将几乎所有内容都设为私有或最终的——或者另一方面,让它们没有 API 文档——是阻碍开源的可扩展性和反馈的一种方式。您的代码可以以您甚至没有想到的方式使用,当所有内容都被锁定时,情况可能并非如此(例如,C# 中的默认密封方法)。
Well, in fact you can have everything public and it doesn't break encapsulation when you state clearly, what is the contract, the correct way to use objects. Maybe not attributes, but methods are often more hidden than they have to be.
Remember, that it is not you, the API designer, that is breaking the encapsulation by making things public. It is the users of the class that can do so, by calling internal methods in their application. You can either slap their hands for trying to do so (i.e. declaring methods private), or pass the responsibility to them (e.g. by prefixing non-API methods with "_"). Do you really care whether someone breaks his code by using your library the other way you advice him to do? I don't.
Making almost everything private or final -- or leaving them without API documentation, on the other hand -- is a way of discouraging extendability and feedback in open source. Your code can be used in a ways you even didn't think of, which might not be the case when everything is locked (e.g. sealed-by-default methods in C#).
您可能遇到的唯一问题是,如果您不使用私有或受保护或抽象静态最终界面,人们会认为您“不酷”强>或其他什么。这些东西就像名牌服装或苹果产品——人们购买它们不是因为需要,而是为了跟上别人的步伐。
是的,封装是一个重要的理论概念,但在实践中“私有”和朋友很少有意义。它们可能在 Java 或 C# 中有意义,但在像 PHP 这样的脚本语言中使用“私有”或“受保护”是完全愚蠢的,因为发明封装是为了由编译器检查,而编译器并不能检查封装。 PHP 中不存在。 更多详情。
另请参阅这个出色的回复以及@troelskn和@马里奥评论此处
The only problem you can run into is that people will see you as "uncool" if you don't use Private or Protected or Abstract Static Final Interface or whatever. This stuff is like designer clothes or Apple gadgets - people buy them not because they need to, but just to keep up with others.
Yes, encapsulation is an important theoretical concept, but in the practice "private" and friends rarely make sense. They might make some sense in Java or C#, but in a scripting language like PHP using "private" or "protected" is sheer stupid, because encapsulation is invented to be checked by a compiler, which doesn't exist in PHP. More details.
See also this excellent response and @troelskn and @mario comments over here
可见性只是您可以为了您自己的利益使用的东西,以帮助您不破坏自己的代码。如果您正确使用它,您将帮助其他人(正在使用您的代码)不会破坏自己的代码(通过不正确使用您的代码)。
在我看来,最简单、广为人知的例子是单例模式。这是一种模式,因为这是一个常见问题。 (维基百科的模式定义:
维基百科中单例模式的定义:
http://en.wikipedia.org/wiki/Singleton_pattern
该模式的实现使用 <强>私有构造函数。如果您不将构造函数设为私有,任何人都可能错误地创建一个新实例,并破坏只有一个实例的全部意义。
The visibility is just something that you can use for your own good, to help you not break your own code. And if you use it right, you will help others (who are using your code) that don't break their own code (by not using your code right).
The simplest, widely known example, in my opinion is the Singleton pattern. It's a pattern, because it's a common problem. (Definition of pattern from Wikipedia:
Definition of the Singleton pattern in Wikipedia:
http://en.wikipedia.org/wiki/Singleton_pattern
The implementation of the pattern uses a private constructor. If you don't make the constructor private, anyone could mistakenly create a new instance, and break the whole point of having only one instance.
您可能认为前面的答案是“理论上的”,如果您在 Doctrine2 实体中使用
public
属性,则会破坏延迟加载。You may think that the previous answers are "theoretical", if you use
public
properties in Doctrine2 Entities, you break lazy loading.为了拯救你自己!
上面有一些很好的答案,但我想补充一点。这称为最小权限原则。特权越少,有权破坏事物的实体就越少。破坏东西是不好的。
如果您遵循最小特权原则,则最小知识原则(或德米特法则)和单一责任原则也不甘落后。由于您编写的用于下载最新足球比分的类遵循了这一原则,并且您必须轮询其数据而不是将其直接转储到您的界面,因此您将整个类复制并粘贴到您的下一个项目中,从而节省了开发时间。节省开发时间是件好事。
如果幸运的话,在创建 gigaquads 的钱。未来的自己会因为不遵守上述原则而徒劳地使用前一个自己的名字,并且他将成为违反 最小惊讶原则。也就是说,您的 bug 是足球比分模型中的解析错误,但由于您没有遵循 LOD 和 SRP,因此您会惊讶地发现您正在与输出生成内联进行 XML 解析。生活中有很多比你自己的代码的可怕性更令人惊讶的事情。相信我,我知道。
由于您遵循了所有原则并记录了代码,因此您每周四下午都会花两个小时进行维护编程,其余时间则用于冲浪。
To save you from yourself!
There's been some excellent answers above, but I wanted to add a bit. This is called principle of least privilege. With less privilege, less entities have authority to break things. Breaking things is bad.
If you follow the principle of least privilege, the principle of least knowledge (or Law of Demeter) and single responsibility principle aren't far behind. Since your class you wrote to download the latest football scores has followed this principle, and you have to poll it's data instead of it being dumped straight to your interface, you copy and paste the whole class into your next project, saving development time. Saving development time is good.
If you're lucky, you'll be coming back to this code in 6 months to fix a small bug, after you've made gigaquads of money from it. Future self will take prior self's name in vain for not following the above principles, and he will fall victim to a violation of the principle of least astonishment. That is, your bug is a parse error in the football score model, but since you didn't follow LOD and SRP, you're astonished at the fact that you're doing XML parsing inline with your output generation. There are much better things in life to be astonished by than the horrificness of your own code. Trust me, I know.
Since you followed all the principles and documented your code, you work two hours every Thursday afternoon on maintenance programming, and the rest of the time surfing.