为什么方法和属性的可见度很重要?
为什么不能让所有的方法和属性都可以从任何地方访问?
如果我宣称属性是public
你能给我一个我可以遇到的问题的例子吗?
把能见度范围看作是信任的内部圈子。
以自己为例,思考哪些活动是公开的,什么是私人的或受保护的。 有些事情是你没有委托给任何人代表你做的。 有一些是很好的触发,有一些有限的访问。
同样,在编程中,范围为您提供了创build不同信任圈的工具。 另外,让事情变得私密/保护,让你更好地控制发生的事情。 例如,您可以允许第三方插件扩展您的一些代码,但可以将其限制在可以使用的范围内。
所以,概括来说,范围给你提供了额外的安全级别,使得事情更加有条理,否则就会变得更加有组织。
把麦当劳当作一个对象。 有一个众所周知的公共方法来订购一个BigMac。
在内部,将会有几十亿个其他的调用来实际获得制作Bigmac的材料。 他们不希望你知道他们的供应链是如何工作的,所以你得到的是公共的Gimme_a_BigMac()
调用,永远不会允许你访问Slaughter_a_cow()
或者Buy_potatoes_for_fries()
方法。
为了你自己的代码,没有人会看到,继续前进,把所有东西都公之于众。 但是,如果你正在为其他人重新使用一个图书馆,那么你就去保护内部细节。 这使得麦当劳可以自由地转向使Scotty束过肉饼,而不必调用一家货运公司来陆路运送肉类。 最终用户从来不知道其中的差别 – 他们只是得到他们的BigMac。 但内部一切都可能从根本上改变。
为什么不能让所有的方法和属性都可以从任何地方访问?
因为那太贵了 。
我所做的每个公共方法都必须经过精心devise ,然后由build筑师团队批准 ,但必须在面对任意敌对或错误的呼叫者时加以实施 ,必须经过充分testing ,testing期间发现的所有问题必须添加回归套件 ,方法必须logging ,文件必须翻译成至less十二种不同的语言。
但最大的代价是: 这个方法必须永远保持不变,永远不变 。 如果我在下一个版本中决定我不喜欢这种方法,我不能改变它,因为客户现在依靠它。 打破公共方法的向后兼容性会给用户带来成本 ,我不喜欢这样做。 生活在糟糕的devise或公共方法的实施中会给下一版本的devise者,testing者和实施者带来很高的成本。
公开的方法很容易花费数千甚至数万美元。 在一个class级上百名学生,这是一百万美元的课程。
私人方法没有这些成本。 明智地支付股东的钱; 尽一切可能的私人。
因为这违反了封装的概念,这是OOP的一个关键原则。
你说,你跑的风险?
<?php class Foo { /** * @var SomeObject */ public $bar; }
你的代码指出$bar
应该包含一个对象instanceof SomeObject
。 但是,任何使用你的代码的人都可以
$myFoo->bar = new SomeOtherObject();
…任何依靠Foo :: $ bar作为SomeObject
都会中断。 有了getter和setter以及受保护的属性,你可以执行这个期望:
<?php class Foo { /** * @var SomeObject */ protected $bar; public function setBar(SomeObject $bar) { $this->bar = $bar; } }
现在你可以肯定的是,任何时候Foo :: $ bar都被设置了,它将会是SomeObject
对象的一个实例。
通过隐藏实现细节,也可以防止对象进入不一致的状态。
这是一个人为的例子(伪代码)。
public class Stack { public List stack = new List(); public int currentStackPosition = 0; public String pop() { if (currentStackPosition-1 >= 0) { currentStackPosition--; return stack.remove(currentStackPosition + 1); } else { return null; } } public void push(String value) { currentStackPosition++; stack.add(value); } }
如果你让这两个variables是私有的,那么实现就可以正常工作 但是,如果公开,只需要为currentStackPosition设置一个不正确的值或者直接修改列表就可以轻松破解它。
如果您只公开这些function,那么您就提供了一个可靠的合同,以便他人可以使用和信任。 揭露这个实现只是让它成为一件没有任何问题的东西。
任何语言都不需要封装,但是非常有用。 封装被用来最小化潜在的依赖关系的数量和最高的变化传播概率,同时也有助于防止不一致:
简单的例子:假设我们做了一个包含四个variables的矩形类 – 长度,宽度,面积,周长。 请注意,面积和周长是从长度和宽度(通常我不会为它们制作variables)导出的,所以长度的变化会改变面积和周长。
如果没有使用正确的信息隐藏(封装),那么使用该Rectangle类的另一个程序可以改变长度而不改变该区域,并且将会有不一致的Rectangle。 如果没有封装,就可以创build一个长度为1,宽度为3的矩形,面积为32345。
使用封装,我们可以创build一个函数,如果一个程序想要改变矩形的长度,那么该对象将适当地更新其面积和周长而不会不一致。
封装消除了不一致的可能性,并将保持一致的责任转移到对象本身上,而不是使用它的程序。
然而与此同时,封装有时是一个坏主意 ,而运动规划和碰撞(在游戏编程中)则是特别可能出现这种情况的领域。
问题是封装在需要的地方是非常好的,但是当它被用在不需要的地方,比如当有全局属性需要由一组封装来维护时,它是非常糟糕的,因为OOP强制封装不pipe怎样,你被困住了。 例如,非本地对象有许多属性,例如,任何types的全局一致性。 在OOP中会发生的事情是,每个对象都必须对全局一致性条件进行编码,并尽全力帮助维护正确的全局属性。 如果你真的需要封装,这可以很有趣,允许其他的实现。 但是如果你不需要它,你最终会在很多地方写很多非常复杂的代码,这些代码基本上都是一样的。 一切似乎都被封装起来,但实际上是完全相互依赖的。
那么事实上,你可以公开所有的东西,当你清楚地陈述什么是契约,使用对象的正确方法时,它不会破坏封装。 也许不属性,但方法往往比他们必须更隐藏。
请记住,这不是你,APIdevise者,通过公开事物来打破封装。 通过在应用程序中调用内部方法,可以这样做的是类的用户。 你可以为了试图这样做(即声明私有方法),或者把责任传递给它们(例如,通过在非API方法前添加“_”)。 你真的在乎是否有人用你build议的另一种方式使用你的图书馆来破坏他的代码吗? 我不。
另一方面,几乎所有的事情都是私有的或者最终的 – 或者在没有API文档的情况下离开它们 – 是一种阻止在开源中扩展和反馈的方式。 你的代码可以以你甚至没有想到的方式使用,当所有东西都被locking的时候(例如在C#中使用默认方法进行密封),情况可能不是这样。
你唯一可能遇到的问题是,如果你不使用私有或受保护或抽象静态最终接口或其他什么,人们会看到你“不酷”。 这件东西就像名牌服装或苹果产品 – 人们不是因为需要购买,而是为了跟上他人。
是的,封装是一个重要的理论概念,但在实践中“私人”和朋友很less有意义。 他们可能在Java或C#中有一定的意义,但是在像PHP这样的脚本语言中,使用“private”或“protected”是很愚蠢的,因为封装是由编译器发明的,PHP中不存在。 更多细节 。
另请参阅这个优秀的回应和@troelskn和@mario评论在这里
可见性只是为了您自己的利益而使用的 ,以帮助您不破坏自己的代码。 如果你使用正确的话,你会帮助别人(谁在使用你的代码)不破坏自己的代码(通过不使用你的代码)。
在我看来,最简单的,广为人知的例子是Singleton模式。 这是一个模式,因为这是一个普遍的问题。 (来自维基百科的pattern的定义:
是loggingdevise问题解决scheme的正式方式
在Wikipedia中的单例模式的定义:
在软件工程中,单例模式是一种用于实现单例math概念的devise模式,通过将类的实例化限制为一个对象。 当需要恰好一个对象来协调系统中的操作时,这非常有用。
http://en.wikipedia.org/wiki/Singleton_pattern
该模式的实现使用私有构造函数。 如果你不把构造函数私人化,任何人都可能会错误地创build一个新的实例,并打破只有一个实例的全部观点。
你可能会认为以前的答案是“理论上的”,如果你在Doctrine2实体中使用public
财产,你打破懒惰加载。
为了拯救你自己!
上面有一些很好的答案,但我想补充一点。 这被称为最小特权原则 。 用较less的特权,较less的实体有权破坏事物。 打破事情是不好的。
如果遵循最小特权的原则,那么最小知识 (或德米特法)和单一责任原则的原则就不远了。 由于你写下载最新足球比分的课程遵循这个原则,你必须轮询它的数据,而不是直接扔到你的界面,你复制和粘贴到下一个项目,节省开发时间。 节省开发时间是好的。
如果你幸运的话,你将在6个月后回到这个代码来修复一个小错误,在你赚了1000亿美元之后。 未来的自我会因为没有遵循上述原则而徒劳无功,他将成为违反最不惊奇原则的受害者。 也就是说,你的bug是足球比分模型中的一个分析错误,但是因为你没有遵循LOD和SRP,所以你对于你的输出生成XML内容的事实感到惊讶。 人生中有很多令人惊讶的事情,而不是你自己的代码的可怕之处。 相信我,我知道。
由于您遵循了所有原则并logging了您的代码,因此您每周四下午在维护编程中工作两个小时,其余时间则在冲浪。