数据抽象
数据抽象是指对外只提供基本信息并且隐藏他们的背景细节,即只呈现程序中所需的信息而没有提供细节。
数据抽象是一种编程(和设计)技术,依赖于接口和实现的分离。
让我们举一个现实生活中的例子。一个电视,你可以打开和关闭,改变频道,调整音量,并添加外部组件,比如扬声器,录像机,以及 DVD 播放器。但是你不知道它的内部细节,也就是说,你不知道它如何通过无线技术或者通过电缆接收信号,如何转化信号,以及最后将信号显示在屏幕上。
因此,我们可以说电视机实现了外部接口与内部实现的清晰分离,你可以无需知道它的内部具体实现,就可以通过其外部接口比如电源按钮,更换频道,音量控制。
现在,如果我们谈论的是 C++ 编程, C++ 类提供了大量数据抽象的例子。他们提供了大量的针对外部世界的公有函数来满足对象的功能或者操作对象数据,即外部函数不知道类在内部是如何实现的。
例如,你的程序可以在不知道函数实际使用什么算法来对给定的值进行排序的情况下调用 sort() 函数。事实上,排序功能的底层实现可以在不同版本之间变化,只要接口保持不变,你的函数调用将仍然起作用。
在 C++ 中,我们使用类来定义自己的抽象数据类型( ADT )。您可以使用类 ostream 的 cout 对象对流数据进行标准输出如下:
#include <iostream>
using namespace std;
int main( )
{
cout << "Hello C++" <<endl;
return 0;
}
在这里,你不需要了解 cout 如何在用户的屏幕上显示文本。你只需要知道的公共接口和的 cout 底层实现是可以自由改变的。
访问标号实施抽象
在 C++ 中,我们使用访问标号定义抽象接口类。一个类可以包含零个或多个访问标签:
- 成员定义了一个公有标号,程序的所有部分都可以访问这个公共标号。类型的数据抽象视图由其公有成员定义。
- 使用类的代码不可以访问带有私有标号的成员。对于使用类的代码,私有部分隐藏了类的实现细节。
一个访问标号可以出现的次数通常是没有限制的。每个访问标号指定了随后的成员定义的访问级别。这个指定的访问级别持续有效,知道遇到下一个访问标号或看到类定义提的右花括号为止。
数据抽象的好处
数据抽象提供了两个重要的优势:
- 避免内部出现无意的,可能破坏对象状态的用户级错误。
- 随着时间的推移类实现可能会根据需求或缺陷报告来做出修改,但是这种修改无需改变用户级代码。
通过只在类的私有部分定义数据成员,类作者可以自由的对数据进行更改。如果实现更改,只需要检查类的代码看看这个改变可能造成什么影响。如果数据是公开的,那么任何可以直接访问旧的数据成员的函数都可能遭到破坏。
数据抽象举例
任何一个用公有和私有成员实现一个类的 C++ 程序都是数据抽象的一个例子。考虑下面的例子:
#include <iostream>
using namespace std;
class Adder{
public:
// constructor
Adder(int i = 0)
{
total = i;
}
// interface to outside world
void addNum(int number)
{
total += number;
}
// interface to outside world
int getTotal()
{
return total;
};
private:
// hidden data from outside world
int total;
};
int main( )
{
Adder a;
a.addNum(10);
a.addNum(20);
a.addNum(30);
cout << "Total " << a.getTotal() <<endl;
return 0;
}
编译和执行上面的代码时,它产生以下结果:
Total 60
上面的类实现了把数字加起来,并且返回总和。公有成员 addNum 和 getTotal 是对外的接口,用户需要知道他们才能使用的类。私有成员 total 是用户不需要知道的,但是它是为保证程序正常运行类必要的。
设计策略
抽象使代码分离成接口和实现。所以在设计你的组件的时候,你必须保持接口独立于实现,因此,你才能做到在改变底层实现时,界面将保持不变。
在这种情况下,无论任何程序使用这些接口,他们不会受到影响,只需要重新编译最新的实现。