天天看点

编程核心概念:模块化、封装、抽象:接口与实现分离

作者:小智雅汇

在计算机科学中,模块化是一个很重要的一个概念,模块化可以将一个复杂系统分而治之,计算机硬件和软件都讲求模块化,模块化可以实现隐藏复杂的内部细节而只暴露给用户易于使用的简单的接口部分,同时,模块化也实现了一种抽象。抽象是指只向外界提供关键信息,并隐藏其后台的实现细节,即只表现必要的信息而不呈现细节。

比如一台电视机,您可以打开和关闭、切换频道、调整音量、添加外部组件(如喇叭、录像机、DVD 播放器),但是您不知道它的内部实现细节,也就是说,您并不知道它是如何通过缆线接收信号,如何转换信号,并最终显示在屏幕上。因此,我们可以说电视把它的内部实现和外部接口分离开了,您无需知道它的内部实现原理,直接通过它的外部接口(比如电源按钮、遥控器、声量控制器)就可以操控电视。

自然,模块化需要由封装来实现。

在C中,函数是一种功能封装,通过将作为接口的函数声明放在头文件中,将函数的实现(定义)放在源文件中,来实现接口与实现的分离,由此,也就实现了一种功能抽象,使用函数的用户只需面对头文件中函数声明,来了解其如何使用,函数的实现部分隐藏在源文件中,函数的使用者可以无须理会,由函数的实现者去实现。例如,您的程序可以调用 sort() 函数,而不需要知道函数中排序数据所用到的算法。实际上,函数排序的底层实现会因库的版本不同而有所差异,只要接口不变,函数调用就可以照常工作。

C++的类实现了一种数据抽象,其封装不仅能够将数据和操作这此数据的函数绑定到一起,还具有保护类的成员通过访问说明符进行修饰而不被随意访问的能力。通过把类的实现细节设置为private,我们就能完成类的封装。封装实现了类的接口和实现的分离。

类封装有两个优点:一是确保用户代码不会无意间破坏对象的状态;二是封装的类的具体实现细节可以随时改变,而无须调整用户级别的代码。

一旦把数据成员定义成private,类的作者就可以比较自由地修改数据了。当实现部分发生改变时,只需要检查类的代码本身以确认这次改变有什么影响,也就是只要类的接口不变,用户代码就无须改变。如果数据是public的,则所有使用了原来数据成员的代码都可能失效。这时我们必须定位并重写所有依赖于旧版本实现的代码,之后才能重新使用该程序。

把数据成员的访问权限设成private还有另外一个好处,能够防止由于用户的原因造成数据被破坏。如果我们发现有程序缺陷破坏了对象的状态,则可以在有限的范围内定位缺陷,因为只有实现部分的代码可能产生这样的错误。因此,将错误的搜索限制在有限范围内将能极大地简化更改问题及修正程序等工作。

作为接口的一部分,构造函数和一部分成员函数应该定义在public说明符之后,以便于用户在类的外部访问。而数据成员和作为实现部分的函数应该跟在private说明符之后,以避免用户程序不经意间修改和破坏它们。

自然数据抽象(抽象数据类型,类)相对于功能抽象(函数),是更高一层的抽象,除了实现隐藏,还有访问权限、创建时自动初始化和基于作用域的资源释放的概念。

C++类的成员函数的实现放在源文件中,而类的定义放在头文件中,头文件暴露给使用类的用户,而用户无须关注作为实现部分的源文件。

在 C++中,我们使用访问标签(private、public)来加强类的封装与抽象,相对于C使用头文件和源文件来隔离接口与实现,显然前者有更强的封装性。使用公共标签定义的成员都可以访问该程序的所有部分。一个类型的数据抽象视图是由它的公共成员来定义的。使用私有标签定义的成员无法访问到使用类的代码。私有部分对使用类型的代码隐藏了实现细节。

把代码分离为接口和实现。所以在设计组件时,必须保持接口独立于实现,这样,如果改变底层实现,接口也将保持不变。在这种情况下,不管任何程序使用接口,接口都不会受到影响,只需要将最新的实现重新编译即可。

#include <iostream>
using namespace std;

class Adder{
public:
    // 构造函数
    Adder(int i = 0)
    {
        total = i;
    }
    // 对外的接口
    void addNum(int number)
    {
        total += number;
    }
    // 对外的接口
    int getTotal()
    {
        return total;
    };
private:
    // 对外隐藏的数据
    int total;
}; // 类的定义可以放到头文件中,类的成员函数也可以在类外定义,放到源文件中
int main( )
{
    Adder a;
    
    a.addNum(10);
    a.addNum(20);
    a.addNum(30);
    
    cout << "Total " << a.getTotal() <<endl;
    return 0;
}
           

ref:

https://www.runoob.com/cplusplus/cpp-data-encapsulation.html

-End-

继续阅读