pax*_*blo 12
封装是一个适度简单的概念,一旦你意识到它(可能)来自与胶囊相同的基本单词.
它只是一个信息包含.
封装意味着一个类只发布其他人使用它所需的内容,而不再发布.这称为信息隐藏,它意味着类可以完全改变其内部,而不会对其任何用户产生影响.
换句话说,字典类可以作为一个简单的数组开始生活,然后进入二进制文本树,甚至可能进入某些数据库访问函数,所有这些都不会改变它的接口.
在面向对象的世界中,对象既包含数据,也包含用于操作数据的方法,这是封装的顶峰.这样做的一种方法是确保每个对象知道要调用哪些函数来操纵其数据,并确保调用正确的函数.
作为一个例子,这里有一个用于在我的神话中维护整数列表的类,但奇怪的是Pythonic,因此很容易理解,语言:
class intlist:
private int val[10]
private bool used[10]
public constructor:
for i in 0..9:
used[i] = false
public function add (int v):
for i in 0..9:
if not used[i]:
used[i] = true
val[i] = v
return
throw intlist-full
public function del (int v):
for i in 0..9:
if used[i] and val[i] == v:
used[i] = false
return
throw intlist-invalid-value
Run Code Online (Sandbox Code Playgroud)
现在,这里发布的唯一信息是构造函数和两个用于添加和删除的函数.
因为其他所有内容都是封装的,所以我可以随意更改它,而不会破坏使用它的代码.
我可以使数组更长,我可以将它们存储在排序或二叉树而不是数组中以使其更快.只要发布的API没有变化,我就可以自由地做我想做的事情.事实上,我还可以在不破坏其他代码的情况下向API添加内容,我只是无法删除或更改他们依赖的任何内容.
您应该注意,封装不是面向对象的新东西.通过确保信息隐藏在模块中(通常是源文件或具有私有标头的组),它已经存在了很长时间.
事实上,这些stdio.h FILE*东西就是一个很好的例子.你不关心指针实际上是什么,因为所有使用它的函数都知道如何做它们的东西.
小智 5
封装不仅仅是为类定义访问器和修改器方法。它是面向对象编程的更广泛的概念,包括最小化类之间的相互依赖性,并且通常通过信息隐藏来实现。
封装的美妙之处在于能够在不影响用户的情况下改变事物。
在像 Java 这样的面向对象编程语言中,您可以通过使用可访问性修饰符(public、protected、private,以及暗示包私有的无修饰符)隐藏细节来实现封装。通过这些可访问级别,您可以控制封装级别,级别限制越少,发生更改时的成本就越高,并且该类与其他依赖类(即用户类、子类)的耦合程度越高。
因此,目标不是隐藏数据本身,而是隐藏如何操作该数据的实现细节。
这个想法是提供一个公共接口,通过它您可以访问这些数据。您稍后可以更改数据的内部表示,而不会影响类的公共接口。相反,通过暴露数据本身,您会损害封装性,从而损害在不影响用户的情况下改变数据操作方式的能力。您创建与数据本身的依赖关系,而不是与类的公共接口的依赖关系。当“改变”最终找到你时,你就会为麻烦调制出完美的鸡尾酒。
您可能希望封装对字段的访问的原因有多种。Joshua Bloch 在他的《Effective Java》一书中的第 14 项:最小化类和成员的可访问性中,提到了几个令人信服的理由,我在此引用:
您可以限制字段中可以存储的值(即性别必须为 F 或 M)。您可以在字段修改时采取操作(触发事件、验证等)。您可以通过同步方法来提供线程安全性。您可以切换到新的数据表示(即计算字段、不同的数据类型)。但是,封装不仅仅是隐藏字段。在 Java 中,您可以隐藏整个类,从而隐藏整个 API 的实现细节。例如,考虑一下 Arrays.asList() 方法。它返回一个List实现,但是你并不关心哪个实现,只要它满足List接口,对吧?将来可以更改实现,而不会影响该方法的用户。
封装之美
现在,在我看来,要真正理解封装,必须首先理解抽象。
例如,考虑汽车概念的抽象级别。汽车的内部实现非常复杂。它们有几个子系统,如传动系统、制动系统、燃油系统等。
然而,我们简化了它的抽象,我们通过其抽象的公共接口与世界上所有的汽车进行交互。我们知道所有的汽车都有一个方向盘,通过它我们可以控制方向,它们有一个踏板,当你按下它时,你可以加速汽车并控制速度,还有一个踏板,当你按下它时,你可以让它停止,并且你有一个齿轮控制杆可以让您控制前进或后退。这些功能构成了汽车抽象的公共接口。早上你可以开轿车,下午就可以下车开SUV,就好像这是一样的事情。
然而,我们很少有人知道所有这些功能是如何在幕后实现的细节。想想汽车没有液压定向系统的时代。有一天,汽车制造商发明了它,并决定从那时起将其应用到汽车中。尽管如此,这并没有改变用户与他们交互的方式。最多,用户体验到了定向系统的使用上的改进。像这样的改变是可能的,因为汽车的内部实现是封装的。可以安全地进行更改,而不会影响其公共接口。
现在,想想汽车制造商决定将油箱盖放在汽车下方,而不是汽车的一侧。你去买了一辆新车,当你的汽油用完时,你去了加油站,却找不到油箱盖。突然您意识到就在汽车下方,但您无法用气泵软管到达它。现在,我们已经打破了公共接口契约,因此,整个世界崩溃了,它分崩离析了,因为事情没有按照预期的方式进行。像这样的改变将花费数百万美元。我们需要更换世界上所有的加油泵。当我们打破封装时,我们必须付出代价。
因此,正如您所看到的,封装的目标是最大限度地减少相互依赖并促进更改。您可以通过最小化实现细节的暴露来最大化封装。类的状态只能通过其公共接口访问。
我强烈推荐您阅读 Alan Snyder 撰写的一篇名为《面向对象编程语言中的封装和继承》的论文。此链接指向 ACM 上的原始论文,但我很确定您可以通过 Google 找到 PDF 副本。