自从有了抽象数据类型的思想指导,突然有种豁然开朗的感觉,感觉这枯燥繁琐的C语言有了生气,大脑思考问题时,增添了不少灵感和思路。面对一个复杂问题,不再迷茫而不知所措。
根据模块化开发的思想,对于一个复杂问题,一般是根据”逐步求精“的指导精神,将问题分析,按功能将程序分解为多个模块,其中包含main函数的称作主模块,其它模块都可看作是主模块的库,甚至有些模块是相对独立的,可以作为多个应用程序的库,以实现最大限度的代码重用。
而这模块的划分就需要根据经验,尽可能地将在复杂的问题中提取多个抽象数据类型,当然这个类型和它的行为就可通过接口来定义,也就是通过一个头文件形式。有了接口,进而再通过底层的实现和表示形式完成接口中定义的功能。
有了这种抽象类型的接口和实现,一方面将某功能模型抽象独立出来,实现更大限度的重用。另一方面,使程序模块化,层次化更清晰,从开发效率和维护方面,好处多多,不再详述
这里我们以扫描器抽象类型为例,扫描器是指将一个字符串分解为标记,其中标记是一连串的字母或数字组成的字符串,或空格或标点组成的单个字符串。这种抽象类型适合很多场合,如对一句话进行词法分析,编译器读取代码语句等场景,这个过程都是必不可少的,有必要将此功能抽象出来,作为相对独立的类型。
代码示例:
1 /* scanner.h --- 2 * 3 * Filename: scanner.h 4 * Description: 扫描器接口 5 * Author: 6 * Maintainer: 7 * Created: 二 8月 14 09:04:30 2012 (+0800) 8 * Version: 9 * Last-Updated: 二 8月 14 16:35:16 2012 (+0800) 10 * By: 11 * Update #: 32 12 * URL: 13 * Keywords: 14 * Compatibility: 15 * 16 */ 17 18 /* Commentary: 19 * 扫描器功能:将一个字符串分解为标记,其标记是下列之一: 20 * 一连串数值字母字符(即字母或数字)或 由空格或标点组成的单个字符的字符串 21 * 22 */ 23 24 /* Code: */ 25 26 #include "../commonlib/genlib.h" 27 28 #ifndef _scanner_h 29 #define _scanner_h 30 31 typedef struct scannerCDT *scannerADT; 32 33 /************************************************************************* 34 *功能描述:初始化一个扫描器 35 *参数列表:line:将用来分解的字符串内容 36 *返回类型: 37 **************************************************************************/ 38 scannerADT InitScanner(string line); 39 40 /************************************************************************* 41 *功能描述:获取下一个标记(token) 42 *参数列表:scanner:扫描器实例 43 *返回类型: 44 **************************************************************************/ 45 string GetNextToken(scannerADT scanner); 46 47 /************************************************************************* 48 *功能描述:判断是否到达尾部 49 *参数列表:scanner:扫描器实例 50 *返回类型: 51 **************************************************************************/ 52 53 bool IsEndOfLine(scannerADT scanner); 54 55 /************************************************************************* 56 *功能描述:释放扫描器实例 57 *参数列表: 58 *返回类型: 59 **************************************************************************/ 60 void FreeScanner(scannerADT scanner); 61 62 #endif 63 64 /* scanner.h ends here */
代码示例:
1 /* scanner.c --- 2 * 3 * Filename: scanner.c 4 * Description: 扫描器的实现 5 * Author: magc 6 * Maintainer: 7 * Created: 二 8月 14 10:49:45 2012 (+0800) 8 * Version: 9 * Last-Updated: 二 8月 14 16:47:49 2012 (+0800) 10 * By: magc 11 * Update #: 254 12 * URL: 13 * Keywords: 14 * Compatibility: 15 * 16 */ 17 18 /* Commentary: 19 * 20 * 21 * 22 */ 23 24 /* Code: */ 25 #include <assert.h> 26 #include <ctype.h> 27 #include <errno.h> 28 #include <limits.h> 29 #include <string.h> 30 #include <stdarg.h> 31 #include <stdlib.h> 32 #include <stdio.h> 33 #include "../commonlib/genlib.h" 34 #include "../commonlib/strlib.h" 35 #include "scanner.h" 36 37 struct scannerCDT{ 38 int pos; //当前游标位置,通过此结构属性来实现状态的封闭,减少使用全局变量 39 string line; //当前字符串(行)的内容 40 int line_len; //当前字符串的总长度 41 }; 42 /************************************************************************* 43 *功能描述:初始化一个扫描器实例 44 *参数列表:line:要分析的字符串 45 *返回类型:返回一个初始化后的扫描器 46 **************************************************************************/ 47 scannerADT InitScanner(string line){ 48 scannerADT scanner = New(scannerADT); 49 scanner->line = CopyString(line); 50 scanner->pos = 0; //通过结构属性,来代替全局变量的封闭状态功能,避免使用全局变量 51 scanner->line_len = StringLength(line); 52 53 return scanner; 54 } 55 56 /************************************************************************* 57 *功能描述:获取下一个标记(token),标点和空格分别作为独立的标记,每一个标记返回一次。 58 *参数列表:scanner:扫描器 59 *返回类型: 60 **************************************************************************/ 61 string GetNextToken(scannerADT scanner ){ 62 63 char buf; 64 65 int start = -1;//记录当前这次单词的起始位置 66 buf = IthChar(scanner->line,scanner->pos); 67 if(isalnum(buf)){ 68 //若字符是字母或数字,则开始用start标记起始,直到遇到非字母和非数字结束,作为单独的标记 69 start = scanner->pos; 70 while(isalnum(buf)){ 71 scanner->pos++; 72 buf = IthChar(scanner->line,scanner->pos); 73 } 74 return SubString(scanner->line,start,scanner->pos-1); 75 } 76 else{ 77 //若字符是非字母或非数字,则作为单个字符组成的字符串标记 78 scanner->pos++; 79 return CharToString(buf); 80 } 81 82 } 83 84 /************************************************************************* 85 *功能描述:判断是否到达行尾部 86 *参数列表: 87 *返回类型: 88 **************************************************************************/ 89 bool IsEndOfLine(scannerADT scanner){ 90 return (scanner->pos >= scanner->line_len-1); 91 92 } 93 /************************************************************************* 94 *功能描述:释放扫描器实例 95 *参数列表: 96 *返回类型: 97 **************************************************************************/ 98 void FreeScanner(scannerADT scanner){ 99 FreeBlock(scanner->line); 100 FreeBlock(scanner); 101 102 } 103 /************************************************************************* 104 *功能描述:主函数 105 *参数列表: 106 *返回类型: 107 **************************************************************************/ 108 int main(int argc, char * argv[]) 109 { 110 string line = "hello ! how are you !\n tiian jin! "; 111 scannerADT scan = InitScanner(line); 112 int i = 1; 113 114 while(!IsEndOfLine(scan)){ 115 printf("第%d个标记:|%s|\n",i,GetNextToken(scan)); 116 i++; 117 } 118 119 FreeScanner(scan);//释放内存空间 120 } 121 122 123 124 /* scanner.c ends here */
注:
1、体会通过接口形式定义扫描器抽象类型的好处
2、体会通过抽象类型的属性来实现状态的封装,避免使用全局变量(使用全局变量的程序很难理解和控制)
3、理解这种接口和实现的职责和界限,接口只限行为,不管底层表示和实现细节,基础同接口,可以有多种实现方式。
在GCC下编译运行结果: