面向对象抽象发生在类级别设计过程中,其目的是为了隐藏API / 设计 / 系统提供的功能是如何被实现的,从而简化访问底层实现的“接口”,简化系统的复杂度。
抽象的过程可以在越来越高层次(层)的类中重复,这使得可以构建大型系统,而不会增加每个层次的代码复杂性和理解难度。
例如,Java开发人员可以使用FileInputStream的高级特性,而不必考虑其工作原理(即文件句柄,文件系统安全检查,内存分配和缓冲区管理将在内部处理并对用户隐藏)。这允许更改FileInputStream
的实现,只要与FileInputStream
交互的API(接口)保持一致,之前版本的代码仍然可以工作。
同样,在设计自己的类时,您将希望尽可能地隐藏内部实现细节,以便他人无法修改。
在Booch的定义中1,面向对象封装通过信息隐藏实现,特别是在控制访问内部数据(表示状态的字段/成员)的同时,防止直接、外部地更改这些字段,并且隐藏类的任何内部实现方法(例如,通过将它们设置为私有)。
例如,类的字段可以默认设为private
,只有在需要外部访问这些字段时,才会从类中公开get()
和/或set()
(或Property
)。 (在现代面向对象语言中,字段可以标记为readonly
/ final
/ immutable
,这进一步限制了对字段的更改,即使在类内部也是如此)。
没有应用信息隐藏的示例(不好的做法):
class Foo {
public int notEncapsulated;
}
应用字段封装的示例:
class Bar {
private int encapsulatedPercentageField;
public void setEncapsulatedField(int percentageValue) {
if (percentageValue >= 0 && percentageValue <= 100) {
encapsulatedPercentageField = percentageValue;
}
}
}
不可变/仅构造函数初始化字段的示例:
class Baz {
private final int immutableField;
public void Baz(int onlyValue) {
immutableField = onlyValue;
}
}
关于抽象化与抽象类
抽象类是指促进类之间共性重用的类,但是抽象类本身不能直接使用new()
实例化 - 抽象类必须被子类化,只有具体的
(非抽象的)子类可以被实例化。一个导致人们混淆Abstraction
与abstract class
的原因是,在面向对象编程的早期阶段,继承更多地被用来实现代码重用(例如通过相关联的抽象基类)。现在,组合通常优于继承,同时还有更多的工具可用于实现抽象化,例如通过接口、事件/委托函数、特征/混入等。
关于封装与信息隐藏
封装的含义似乎随着时间的推移而发生了变化,在最近,封装
也可以常用于更一般的意义上,即确定将哪些方法、字段、属性、事件等打包到一个类中。
引用维基百科的话:
在面向对象编程语言的更具体的环境中, 这个概念被用来指代信息隐藏机制、打包机制,或者两者的结合.
例如,在语句
我已经将数据访问代码封装到它自己的类中了
.. 封装的解释大致相当于关注点分离或者单一职责原则(SOLID中的"S"),也可以说是重构的同义词。
[1] 如果您看过Booch的封装猫图片,您将永远无法忘记封装 - 来自《面向对象分析与设计的应用》第二版的46页