1.通常的解释
你可以不看这一部分的东西。下面摘录的是一些通常的说法:百度百科面向对象技术
【面向对象技术的基本特征主要有抽象性、封装性、继承性和多态性。
封装(Encapsulation)就是把对象的属性和行为结合成一个独立的单位,并尽可能隐蔽对象的内部细节。封装有两个含义:一是把对象的全部属性和行为结合在一起,形成一个不可分割的独立单位。对象的属性值(除了公有的属性值)只能由这个对象的行为来读取和修改;二是尽可能隐蔽对象的内部细节,对外形成一道屏障,与外部的联系只能通过外部接口实现。
封装是通过限制只有特定类的对象可以访问这一特定类的成员,而它们通常利用接口实现消息的传入传出。举个例子,接口能确保幼犬这一特征只能被赋予狗这一类。通常来说,成员会依它们的访问权限被分为3种:公有成员、私有成员以及保护成员。有些语言更进一步:Java可以限制同一包内不同类的访问;C#和VB.NET保留了为类的成员聚集准备的关键字:internal(C#)和Friend(VB.NET);Eiffel语言则可以让用户指定哪个类可以访问所有成员。
封装的信息隐蔽作用反映了事物的相对独立性,可以只关心它对外所提供的接口,即能做什么,而不注意其内部细节,即怎么提供这些服务。例如,用陶瓷封装起来的一块集成电路芯片,其内部电路是不可见的,而且使用者也不关心它的内部结构,只关心芯片引脚的个数、引脚的电气参数及引脚提供的功能,利用这些引脚,使用者将各种不同的芯片连接起来,就能组装成具有一定功能的模块。
封装的结果使对象以外的部分不能随意存取对象的内部属性,从而有效地避免了外部错误对它的影响,大大减小了查错和排错的难度。另一方面,当对象内部进行修改时,由于它只通过少量的外部接口对外提供服务,因此同样减小了内部的修改对外部的影响。同时,如果一味地强调封装,则对象的任何属性都不允许外部直接存取,要增加许多没有其他意义,只负责读或写的行为。这为编程工作增加了负担,增加了运行开销,并且使得程序显得臃肿。为了避免这一点,在语言的具体实现过程中应使对象有不同程度的可见性,进而与客观世界的具体情况相符合。
封装机制将对象的使用者与设计者分开,使用者不必知道对象行为实现的细节,只需要用设计者提供的外部接口让对象去做。封装的结果实际上隐蔽了复杂性,并提供了代码重用性,从而降低了软件开发的难度。
(Stanley B.Lippman / Josee Lajoie / Barbara E.Moo .C++ Primer:人民邮电出版社,2010)】
2.学习思路
3.变化与封装
正如前言中提到的,“面向对象的封装、继承、动态绑定(方法改写)、多态等知识,在编程语言教学中并没有太多的理论含义(编程语言课程不涉及编程语言设计和形式化的内容)”,yqj2065比较轻视这几个概念。所以,封装写的很少。- 如果你的“变化”,是指信息隐藏部分的代码的自由修改,那么能够说封装了“变化”,这是Parnas原则(接口与实现分离)的必然后果。对于数据抽象或类A,用户只能够与其接口交互。只要A保证其接口不变,其内部实现就可以自由地演化。是的,这种自由源于封装,破坏这种自由就破坏了A的封装性。
- 对于(需求)变化的封装,又称开放封闭原则OCP。这里的变化,指原来需求要求处理猫和狗,后来需求要求处理牛、马等。为了封装这种“变化”,我们的代码中将要处理的,是“动物”类型。(另一方面,OCP所言的对修改封闭,也不干涉被隐藏的实现可以自由演化)。
- 类的接口能否修改?关于库(例如其中有类A)的设计,依赖库的程序B要求库是向下兼容的。这里,程序B依赖的是A的接口。不论库的创建者想怎样自由地进行修改与改进A,前提是不得修改A的接口。否则库不是兼容的。A的设计者不可能、也不会“考虑”该服务(的方法名、参数)未来是否保持不变或改变。如果你使用过一些修改过接口的第三方库,你肯定感受颇深。
以JDK中的类为例,类的接口变化时采用的方式:
列举 不赞成使用(Deprecated)的类的接口/方法
Java8 的默认方法为Java接口的变化 提供后悔药。