我知道这个问题可能已经在这个论坛上被问了几千次,甚至net也充满了关于这些概念的许多定义,但所有声音都相同,并且都使用相同的技术词汇。例如,以下定义
封装是将数据和对数据进行操作的代码绑定或包装到单个实体中的过程。这样可以保护数据免受外部接口和误操作的影响。考虑封装的一种方法是作为保护性包装器,防止代码和数据被包装器外部定义的其他代码任意访问。
我从上面的定义中理解的是创建变量,将它们标记为私有并为这些变量生成getter-setter,并使用object来访问那些getter和setter。这样,数据就隐藏在对象内部,只能通过对象访问。 希望我是对的。
抽象是Java中的一个过程,用于隐藏某些细节,仅显示对象的基本功能。换句话说,它处理对象(界面)的外部视图。
现在这是让我困惑的部分。每当我想到抽象时,我想到的东西就是抽象类(可能因为它们都有Abstract关键字)。上面的定义说抽象意味着隐藏数据并且只显示所需的细节,但这是我们在封装中已经做的事情吗?然后有什么区别。另外,我没有得到对象的侧视图它处理对象的外部视图。
有人可以通过一些现实生活中的例子,或者如果可能的话,用一些程序化的例子来更多地阐述这一点。
答案 0 :(得分:82)
OO Abstraction,目的是隐藏如何 API /设计/系统提供的功能的实现复杂性从某种意义上说,实现了简化“接口”以访问底层实现。
抽象过程可以在越来越“更高”的层次(层)上重复,这样就可以构建大型系统,而不会增加代码的复杂性和每层的理解。
例如,Java开发人员可以利用FileInputStream的高级功能,而无需担心它是如何工作的(即文件句柄,文件系统安全检查,内存分配和缓冲将在内部进行管理,并且是隐藏在消费者身上)。这允许更改FileInputStream
的实现,并且只要FileInputStream
的API(接口)保持一致,针对先前版本构建的代码仍然可以工作。
同样,在设计自己的类时,您希望尽可能隐藏其他人的内部实现细节。
在Booch定义 1 中, OO封装是通过Information Hiding实现的,具体而言围绕隐藏类实例拥有的内部数据(表示状态的字段/成员),通过以受控方式强制访问内部数据,防止对这些字段进行直接的外部更改,以及隐藏任何内部实现方法。上课(例如通过将它们设为私有)。
例如,默认情况下,类的字段可以设为private
,并且只有在需要对这些字段进行外部访问时才会get()
和/或set()
(或{ {1}})从课堂上暴露出来。 (在现代OO语言中,字段可以标记为Property
/ readonly
/ final
,这进一步限制了更改,即使在课程中也是如此。)
未应用信息隐藏的示例(不良做法):
immutable
已应用字段封装的示例:
class Foo {
// BAD - NOT Encapsulated - code external to the class can change this field directly
// Class Foo has no control over the range of values which could be set.
public int notEncapsulated;
}
字段的不可变/构造函数初始化示例:
class Bar {
// Improvement - access restricted only to this class
private int encapsulatedPercentageField;
// The state of Bar (and its fields) can now be changed in a controlled manner
public void setEncapsulatedField(int percentageValue) {
if (percentageValue >= 0 && percentageValue <= 100) {
encapsulatedPercentageField = percentageValue;
}
// else throw ... out of range
}
}
Re:抽象与抽象类
Abstract classes是促进类之间共性重用的类,但它们本身不能直接用class Baz {
private final int immutableField;
public void Baz(int onlyValue) {
// ... As above, can also check that onlyValue is valid
immutableField = onlyValue;
}
// Further change of `immutableField` outside of the constructor is NOT permitted, even within the same class
}
实例化 - 抽象类必须是子类,并且只有new()
(非抽象)子类可以实例化。可能concrete
和Abstraction
之间混淆的一个原因是,在OO的早期,继承被更多地用于实现代码重用(例如,使用相关的抽象基类)。如今,composition is generally favoured over inheritance,还有更多可用于实现抽象的工具,例如通过接口,事件/代理/函数,特征/混合等等。
Re:封装与信息隐藏
封装 的含义似乎随着时间的推移而发展,并且在最近的时候,encapsulation
通常也可以在更一般意义上用于确定捆绑到类中的方法,字段,属性,事件等。
引用维基百科:
在面向对象编程语言的更具体的设置中,概念用于表示信息隐藏机制,捆绑机制或两者的组合。
例如,在声明中
我已将 封装 数据访问代码放入自己的类中
.. 封装的解释大致相当于Separation of Concerns或Single Responsibility Principal(SOLID中的“S”),可以说是同义词用于重构。
[1] 一旦你看到Booch的encapsulation cat picture,你就永远无法忘记封装 - 面向对象分析和应用程序设计的p46, 第二版
答案 1 :(得分:49)
简单来说: 在决定实现什么时,你会做抽象。 在隐藏已实现的内容时进行封装。
答案 2 :(得分:32)
抽象是关于识别共性并减少您必须在代码的不同级别使用的功能。
e.g。我可能有一个Vehicle
课程。 Car
来自Vehicle
,Motorbike
也是Vehicle
。我可以向每个Cars
询问车轮,乘客等的数量,并且该信息已被抽象并标识为Motorbikes
和Vehicles
的常见信息。
在我的代码中,我通常可以通过常用方法go()
,stop()
等来处理Scooter
。当我稍后添加新的Vehicle类型时(例如Scooter
)我的大部分代码都不会忽视这一事实,仅Scooter
的实现就会担心{{1}}的特殊性。