跳到主要内容

program-design

模块

设计C程序(或者其他任何语言的程序)时,最好将它看作是一些独立的模块。

模块:一组服务的集合,其中一些服务可以被程序其他部分(称作客户)使用。

每个模块都有一个借口来描述所提供的服务。模块的细节(包括这些服务自身的源代码)都包含在模块的实现中。

C库本身就是一些模块的集合。

将程序分割成模块有一系列的好处:

  • 抽象:我们找到模块会做什么,但是不知道模块怎么实现的;
  • 可复用性
  • 可维护性:错误更容易定位和修复

模块应该:

  • 高内聚:模块中的元素应该彼此紧密相关;
  • 低耦合:模块之间相互独立

模块的类型可以划分为:

  • 数据池:数据池是一些相关的常量或变量的集合。C中通常是一些头文件。建议不要讲变量放在头文件中,而常量放到头文件
  • 库:库是一个相关函数的集合
  • 抽象对象:对隐藏的数据结构进行操作的函数集合
  • 抽象数据类型:将具体数据实现方式隐藏起来的数据类型

信息隐藏

设计良好的模块通常会对它的客户隐藏一下信息(具体实现)。

好处是:

  • 安全性
  • 灵活性

通常会通过static进行实现:

#define PUGLIC /* empty */
#define PRIVATE static

PRIVATE int top = 0;

PUBLIC bool is_empty(void);

抽象数据类型

抽象对象的模块不能拥有该对象的多个实例,我们需要进一步提炼,得到抽象数据类型,这样就可以得到任意个该类型的对象。

因为我们不想让顾客知道具体的数据结构和实现,那么就没办法写下面的代码:

struct t;   //声明但是不定义

struct t s; //抛错

但是可以写这样的代码

// XXX.h 中
typedef struct t* T; // T 为指向 t结构体类型的指针
void destory(T t);

// XXX.c中
struct t {
// ...
}

抽象数据类型的设计问题

  • 命名惯例:一般会常用create等函数名。但是有多个数据类型就需要在前面加上类型名本身已区别,如stack_create;
  • 错误处理:一个可选的方案是加上函数返回值标志操作结果,或者返回零值。