C++中的Modularization
C++的一个主要特点就是Modularization(模块化),比如说,我们可以只了解一个函数的接口的具体定义,而不了解它是怎样实现的,就能够很好的使用它。类似的,即使程序的一个部件是由多个函数组成,或者其中既有自定义类型,也有全局变量,还有函数,但我们可以这样来设想:如果这样的部件也像函数那样有一个起包装作用的接口,也同样可以只需要了解接口,而不需要了解实现,就能够很好的使用它。
若程序中的一个部件具有明确的边界,能够实现接口与实现的分离,并对它的用户而言在使用时只需关心其接口而不管其实现者,就叫做模块(Modularization)。
实现模块的接口与实现的分离,需要程序设计语言提供相应的支持机制。C++提供的支持机制是:
- Namespace
- Class
模块用接口隐蔽了数据和函数的处理细节(我也叫做封装,Encapsulation),使得模块可以在保持接口不变的前提下,改变数据的结构和函数的处理细节。
Namespace
- Namespace是一种表现逻辑聚集关系的机制。i.e. 如果一些声明在逻辑上都与某个准测有关,就可以把这些声明放入一个共同的namespace,以表现这一事实。
- 同一namespace中的声明在概念上属于同一个逻辑实体。
这种利用namespace将函数聚集起来放在一个模块中的行为,我们就可以称之为模块化。namespace是一个名字空间,它拥有封装的属性,因此它也代表了一个模块。
但我们同时也可以看到,将函数定义在namespace中,似乎这个结构也变得模糊起来了,并没有很好的完成我们的预期。因此,我们有另一种方法将界面(Interfaces)与实现(Implementations)分离开来。
我们要实现一点:实现namespace的接口与实现分离的关键,是在其实现部分中出现的成员被该namespace的名字所约束(qualified),这样的约束通过约束符(qualifier::)来表示。
由于各个namespace之间经常会出现互相使用对方成员的情况,如果一使用就要约束,既繁琐又容易出错。因此,C++提供了几种“有限的统一”约束的机制。
- 在成员的实现中对特定namespace的特定成员分别使用using声明,约束范围在该实现内
- 在接口中对特定namespace的特定成员分别使用using声明,约束范围在该namespace的所有实现内:
- 在接口中对特定namespace的所有成员使用using namespace,约束范围在该namespace的所有实现内。
我们可以很直观的从上面的三个例子中看出三种方法的区别,三种方法中,个人认为第三种最好。
接下来讨论另外一个问题:面对不同的用户,我们需要不同的Interfaces,我们要怎样满足所有的users呢?
我们可以通过定义不同的namespace,但实际上使用同样的implementation,以减少不必要的依赖。
总结
通过以上学习,我们可以总结出有关namespace的要点:
- Namespace 引入了成员和接口的概念
- 成员可以是数据,也可以是函数
- 成员的概念将来在类的概念中还会出现
- 接口的概念是由函数的非定义声明发展而来的,注意对照二者(接口与非定义声明)的异同。
- 约束符的引入,使得Namespace的接口和实现能够分离。这种符号将来在类的实现中还要遇到。