Lecture 09 类作为模块
什么是模块?
模块是指:从物理上对程序中定义的实体进行分组,是可以单独编写和编译的程序单位。
模块是组织和管理大型程序的一个重要手段。
一个模块包含接口和实现两部分:
- 接口:是指在模块中定义的、可以被其它模块使用的一些程序实体的描述。
- 实现:是指在模块中定义的所有程序实体的具体实现描述。
对于C语言程序中的模块:
-
接口:包含被外界使用的类型定义、常量定义以及全局变量和函数的声明。(.h文件)
-
实现:包含本模块中所有的类型、常量、全局变量和函数的定义。(.c文件)
如何划分模块
划分模块的基本准则
-
内聚性最大:模块内的各实体之间联系紧密。
-
耦合度最小:模块间的各实体之间关联较少。
-
便于程序的设计、理解和维护,能够保证程序的正确性。
过程式程序的模块划分
-
通常是基于子程序,把共同完成某独立功能的或使用相同数据的子程序及相关的实体放在一起构成模块。
-
缺点是模块边界模糊:
- 一个子程序可能参与几个功能。
- 一个子程序可能使用多个数据集。
类作为模块
在面向对象程序中,类是一个自然的模块划分单位,模块边界比较清晰。
一个C++程序的模块由两部分构成:
-
接口:类的定义,存放在一个.h文件中
-
实现:类的实现(主要是类中成员函数),存放在一个.cpp文件中。
良好的面向对象程序设计风格
结构化程序设计为过程式程序设计提供了一种良好的风格指南,它要求程序单位具有“单入口/单出口”性质,具体体现为:
-
不带goto的顺序、分支和循环流程控制。
-
子程序
良好的面向对象程序设计风格是什么呢?
-
模块间的耦合度反映在对象类之间的关联性上。
-
要降低模块间的耦合度,可以对类中成员函数能访问的对象的集合作一定的限制,并尽量使该集合为最小。
-
上述要求被称为Demeter法则。
Demeter法则(Law of Demeter)
一个类的成员函数
-
除了能访问自身类结构的直接子结构(本类的数据成员)外,不能以任何方式依赖于任何其它类的结构。
-
只应向某个有限集合中的对象发送消息。
“仅与你的直接朋友交谈!”
class Student
{ ... id; ... name;
......
void print();
};
class Class
{ ... id; ... name;
Student student_list[100];
......
void print();
Student& get(int i);
};
class School
{ ... id; ... name;
Class class_list[300];
......
void f()
{ ... id, name ... //OK
... class_list[i].id ,class_list[i].name ... //Not OK,非直接子结构
class_list[i].print(); //OK
class_list[i].get(j).print(); //Not OK,Student不是直接朋友
}
};
Demeter法则有两种表达形式:
-
类表达形式:一个类中的成员函数能访问(向其发消息)的对象所属类的集合。
-
对象表达形式:一个类中的成员函数能访问(向其发消息)的对象的集合。
-
前者适合于静态类型的面向对象语言(如:C++),它可以在编译时刻检查程序是否满足法则;后者则适合于动态类型的面向对象语言(如:Smalltalk),它需要在运行时刻检查程序是否满足法则。
Demeter法则的类表达形式
对于类C中的任何成员函数M,M中只能向以下类的对象发送消息:
-
类C本身。
-
成员函数M的参数类。
-
M或M所调用的成员函数中创建的对象所属的类。
-
全局对象所属的类。
-
类C的成员对象所属的类。
Demeter法则的对象表达形式
对于类C中的任何成员函数M,M中只能向以下的对象发送消息:
-
this指向的对象。
-
成员函数M的参数对象。
-
M或M所调用的成员函数所创建的对象。
-
全局变量中包含的对象。
-
类C的成员对象。