面向对象编程(object-oriented programming),是一种设计思想或者架构风格,而非仅仅是编码方式,OOP程序也并不一定要用OOP语言来写。
OOP拥有封装,继承与多态三大特性,但 $OOP \not= 封装+继承+多态$。
封装是想把一段逻辑/概念抽象出来做到”相对独立”。多态是指让一组Object表达同一概念,并展现不同的行为。继承是希望通过类型的is-a
关系来实现代码的复用。
在c语言里运用面向对象
c语言有哪些方法来支持OOP编程呢?
struct
结构体,可以将对象的状态和行为封装在一起。- 函数当然也可以封装,实现简洁的接口与复杂的内部隐藏,函数指针关联对象的行为。
static
关键字可以控制变量与函数的作用域,避免命名冲突与链接错误。const
关键字约束一些变量的行为。- 类型转换,
void
类型转换,结构体类型转换一定程度上可以模拟多态与继承。 - 一些命名规范也可以起到访问控制的说明。
#define
宏语法实现的代码复用。
首先看一个基础的的栈实现方式:
stack.h头文件
1 |
|
2 |
|
3 | |
4 | bool push(int iVal); |
5 | bool pop(int *pRet); |
6 | |
7 |
|
stack.c实现
1 |
|
2 |
|
3 | |
4 | /* 全局变量 */ |
5 | int buf[16]; |
6 | int top = 0; |
7 | |
8 | bool isStackFull() { |
9 | return top == sizeof(buf) / sizeof(int); |
10 | } |
11 | |
12 | bool isStackEmpty() { |
13 | return top == 0 |
14 | } |
15 | |
16 | bool push(int iVal) { |
17 | if(isStackFull()) |
18 | return false; |
19 | buf[top++] = val; |
20 | return true; |
21 | } |
22 | |
23 | bool pop(int *pRet) { |
24 | if(isStackEmpty()) |
25 | return false; |
26 | *pRet = buf[--top]; |
27 | return true; |
28 | } |
使用面向对象的思想来优化这个栈,并且增加来了两个检查功能,第一个检查:栈中存储的值要在一定的范围内,第二个检查:每次push到栈中的数据必须比上一次的值大。
1 |
|
2 |
|
3 | |
4 |
|
5 | extern "C" { |
6 |
|
7 | |
8 | /* 校验器结构,检查器父类型接口 */ |
9 | typedef struct Validator { |
10 | bool (* const validate) (struct Validator *pThis, int val); /* 校验器函数指针,具体的校验操作 */ |
11 | void * const pData; /* void类型数据可以转换为Range或者PreviousValue类型 */ |
12 | } |
13 | |
14 | /* 范围检查的最大最小值 */ |
15 | typedef struct { |
16 | const int min; |
17 | const int max; |
18 | } Range; |
19 | |
20 | /* 保留每次存入的上一个值,进行比较 */ |
21 | typedef struct { |
22 | int previousValue; |
23 | } PreviousValue; |
24 | |
25 | /* 栈对象 */ |
26 | typedef struct { |
27 | int top; |
28 | const size_t size; |
29 | int * const pBuf; |
30 | Validator * const pValidator; /* 校验器常量指针,指针是常量不能修改,所指对象可修改 */ |
31 | } Stack; |
32 | |
33 | /* 使用宏定义赋值/初始化接口 */ |
34 |
|
35 | 0, sizeof(buf) / sizeof(int), (buf), \ |
36 | NULL \ |
37 | } |
38 | |
39 |
|
40 | validateRange, \ |
41 | pRange \ |
42 | } |
43 | |
44 |
|
45 | validatePrevious, \ |
46 | pPrevious \ |
47 | } |
48 | |
49 |
|
50 | 0, sizeof(buf) / sizeof(int), (buf), \ |
51 | pValidator \ |
52 | } |
53 | |
54 | bool validateRange(Validator *pThis, int iVal); |
55 | bool validatePrevious(Validator *pThis, int iVal); |
56 | |
57 |
|
58 | } |
59 |
|
60 | |
61 |
|
1 |
|
2 |
|
3 | |
4 | static bool isStackFull(const Stack *p) { /* 指向Stack常量的指针,所指对象不可修改 */ |
5 | return p->top == p->size; |
6 | } |
7 | |
8 | static bool isStackEmpty(const Stack *p) { |
9 | return p->top == 0; |
10 | } |
11 | |
12 | /* 范围校验器 */ |
13 | bool validateRange(Validator *pThis, int iVal) { |
14 | Range *pRange = (Range *)(pThis->pData); |
15 | return pRange->min <= iVal && val <= pRange->max; |
16 | } |
17 | |
18 | /* 与上一个值对比检查器 */ |
19 | bool validatePrevious(Validator *pThis, int iVal) { |
20 | PreviousValue *pPrevious = (PreviousValue *)pThis->pData; |
21 | if(iVal < pPrevious->previousValue) |
22 | return false; |
23 | pPrevious->previousValue = iVal; /* 保留当前值为下一次的上一个值 */ |
24 | return true; |
25 | } |
26 | |
27 | /* 模拟了多态性,两个检查器的通用接口,从调用者的角度不知道内部实际处理 */ |
28 | bool validate(Validator *p, int iVal) { |
29 | if(!p) |
30 | return true; |
31 | return p->validate(p, iVal); |
32 | } |
33 | |
34 | bool push(Stack *p, int iVal) { |
35 | if(!validate(p->pValidator, iVal) || isStackFull(p)) |
36 | return false; |
37 | p->pBuf[p->top++] = iVal; |
38 | return true; |
39 | } |
40 | |
41 | bool pop(Stack *p, int *pRet) { |
42 | if(isStackEmpty(p)) |
43 | return false; |
44 | *pRet = p->pBuf[--p->top]; |
45 | return true; |
46 | } |
C语言中的一些设计模式
模板方法模式
在C语言中,资源管理是一件繁琐的事,资源分配与释放成对出现(例如fopen
打开文件后在fclose
关闭文件,malloc
分配内存在free
释放掉内存)。而使用资源的具体逻辑代码被夹在管理(分配和释放)资源的代码之间,甚至需要在多处使用资源返回处都需要释放资源。
例如该例子,资源打开释放与逻辑处理相互耦合。
1 | int range(const char *pFname){ |
2 | FILE *fp = fopen(pFname, "r"); |
3 | if(NULL == fp) |
4 | return -1; |
5 | |
6 | /* 使用资源部分 */ |
7 | int min = INT_MAX; |
8 | int max = INT_MIN; |
9 | |
10 | char buf[256]; |
11 | |
12 | while((fgets(buf, sizeof(buf), fp)) != NULL){ |
13 | int value = atoi(buf); |
14 | min = min > value ? value : min; |
15 | max = max < value ? value : max; |
16 | } |
17 | |
18 | fclose(fp); /* 释放资源 */ |
19 | |
20 | return max - min; |
21 | } |
对于这样的程序结构,模板(Template)方法模式非常有效。模板方法将程序中的处理部分作为可以被替代的函数(使用函数指针实现模板方法),将其它处理作为固定部分,使其可以被重复利用。
1 | /* 对象A */ |
2 | typedef struct FileReaderContext{ |
3 | const char * const pFname; /* 文件名,常量指针且指向对象在上下文中保持不变 */ |
4 | void (* const processor)(struct FileReaderContext *pThis, FILE *fp); /* 具体的逻辑处理部分 */ |
5 | } FileReaderContext; |
6 | |
7 | /* 继承对象A */ |
8 | typedef struct{ |
9 | FileReaderContext base; |
10 | int result /* 返回值 */ |
11 | }MyFileReaderContext; |
12 | |
13 | static void calc_range(FileReaderContext *p, FILE *p){ |
14 | MyFileReaderContext *pCtx = (MyFileReaderContext *)p; /* 结构体类型转换 */ |
15 | pCtx->result = range_processor(fp); |
16 | } |
17 | |
18 | int read_file(FileReaderContext *pCtx){ |
19 | FILE *fp = fopen(pCtx->pFname, "r"); |
20 | if(NULL = fp) |
21 | return -1; |
22 | |
23 | /* processor是calc_range函数 */ |
24 | pCtx->processor(pCtx, fp); |
25 | |
26 | fclose(fp); |
27 | return 0; |
28 | } |
29 | |
30 | int range(const char *pFname){ |
31 | MyFileReaderContext ctx = {{pFname, calc_range}, 0}; |
32 | |
33 | if(read_file(&ctx.base) != 0){ |
34 | fprintf(stderr, "Cannot open file '%s'.\n",pFname); |
35 | } |
36 | |
37 | return ctx.result; |
38 | } |
当需要使用多种资源时,又如何使用模版方法呢?例如:将一个文件中的数据读取至内存,进行排序后在输出至另一个文件的情况。
1 | static long file_size(FILE *fp); /* 求文件大小函数 */ |
2 | |
3 | int process_file( |
4 | const char *pInputFileName, |
5 | const char *pOutputFileName, |
6 | void (*sorter)(void *pBuf)) |
7 | { |
8 | FILE *fpInp = fopen(pInputFileName, "rb"); /* 1. 打开文件 */ |
9 | if(NULL == fpInp) { |
10 | return FILE_OPEN_ERROR; |
11 | } |
12 | |
13 | long size = file_size(fpInp); /* 2. 求文件大小 */ |
14 | |
15 | void *p = malloc(size); /* 3. 分配对应大小内存 */ |
16 | if(NULL == p) |
17 | return NO_MEMORY_ERROR; /* 没有close fpinp的return */ |
18 | |
19 | fread(p,size,1,fpInp); /* 4. 数据读取至内存 */ |
20 | ... |
21 | /* 关闭写入文件,内存中排序,打开写文件,写入排序结构,关闭写文件,释放内存 */ |
22 | } |
处理文件打开的模板方法。
1 | typedef struct FileAccessorContext{ |
2 | const char * const pFname; |
3 | const char * const pMode; |
4 | void (* const processor)(struct FileAccessorContext *pThis, FILE *fp); |
5 | }FileAccessorContext; |
6 | |
7 | bool access_file(FileAccessorContext *pCtx){ |
8 | FILE *fp = fopen(pCtx->pFname, pCtx->pMode); |
9 | if(NULL == fp) |
10 | return false; |
11 | |
12 | pCtx->processor(pCtx,fp); |
13 | |
14 | fclose(fp); |
15 | return ture; |
16 | } |
分配内存的模板方法。
1 | typedef struct BufferContext{ |
2 | void *pBuf; |
3 | size_t size; |
4 | void (*processor)(struct BufferContext *p); |
5 | }BufferContext; |
6 | |
7 | bool buffer(BufferContext *pThis){ |
8 | pThis->pBuf = malloc(pThis->size); |
9 | if(NULL == pThis->pBuf) |
10 | return false; |
11 | |
12 | pThis->processor(pThis); |
13 | |
14 | free(pThis->pBuf); |
15 | return ture; |
16 | } |
获取文件大小的函数。
1 | typedef struct{ |
2 | FileAccessorContext base; |
3 | long size; |
4 | }SizeGetterContext; |
5 | |
6 | /* 求文件大小 */ |
7 | static long file_size(const char *pFname){ |
8 | SizeGetterContext ctx = {{pFname, "rb", size_reader}, 0}; /* 结构体初始化 */ |
9 | |
10 | if(!access_file(&ctx.base)){ /* 打开文件执行size_reader函数 */ |
11 | return -1; |
12 | } |
13 | return ctx.size; |
14 | } |
15 | |
16 | /* 获取文件大小的函数 */ |
17 | static void size_reader(FileAccessorContext *p, FILE *fp){ |
18 | SizeGetterContext *pThis = (SizeGetterContext *)p; |
19 | pThis->size = -1; |
20 | |
21 | if(fseek(fp, 0, SEEK_END) == 0) |
22 | pThis->size = ftell(fp); |
23 | } |
内存排序与错误处理。
1 | typedef enum{ |
2 | ERR_CAT_OK = 0, |
3 | ERR_CAT_FILE, |
4 | ERR_CAT_MEMORY |
5 | }IntSorterError; |
6 | |
7 | typedef struct{ |
8 | const char * const pFname; |
9 | int errorCategory; |
10 | }Context; |
11 | |
12 | typedef struct { |
13 | BufferContext base; |
14 | Context *pAppCtx; |
15 | }MyBufferContext; |
16 | |
17 | IntSorterError int_sorter(const char *pFname){ |
18 | Context ctx = {pFname, ERR_CAT_OK}; |
19 | |
20 | long size = file_size(pFname); /* 执行file_size函数 */ |
21 | if(size == -1){ |
22 | file_error(&ctx); |
23 | return ctx.errorCategory; |
24 | } |
25 | |
26 | MyBufferContext bufCtx = {{NULL, size, do_with_buffer}, &ctx}; /* 结构体初始化*/ |
27 | if(!buffer(&bufCtx.base)){ /* 调用buffer函数,分配内存,执行do_with_buffer函数 */ |
28 | ctx.errorCategory = ERR_CAT_MEMORY; |
29 | } |
30 | |
31 | return ctx.errorCategory; |
32 | } |
33 | |
34 | static void file_error(Context *pCtx){ |
35 | fprintf(stderr, "%s:%s\n", pCtx->pFname, strerror(errno)); |
36 | pCtx->errorCategory = ERR_CAT_FILE; |
37 | } |
38 | |
39 | typedef struct{ |
40 | FileAccessorCOntext base; |
41 | MyBufferContext *pBufCtx; |
42 | }MyFileAccessorContext; |
43 | |
44 | static void do_with_buffer(BufferContext *p){ |
45 | MyBufferContext *pBufCtx = (MyBufferContext *)p; |
46 | MyFileAccessorContext readFileCtx = {{pBufCtx->pAppCtx->pFname,"rb", \ |
47 | reader}, pBufCtx}; |
48 | |
49 | if(!access_file(&readFileCtx.base)){ /* 打开文件,执行reader函数 */ |
50 | file_error(pBufCtx->pAppCtx); |
51 | return; |
52 | } |
53 | |
54 | qsort(p->pBuf, p->size / sizeof(int), sizeof(int), comparator); |
55 | |
56 | MyFileAccessorContext writeFileCtx = {{pBufCtx->pAppCtx->pFname,"wb", \ |
57 | writer},pBufCtx}; |
58 | if(!access_file(&writeFileCtx.base)){ /* 打开文件,执行writer函数 */ |
59 | file_error(pBufCtx->pAppCtx); |
60 | return; |
61 | } |
62 | } |
63 | |
64 | /* 读取内容到内存pBuf中 */ |
65 | static void reader(FileAccessorContext *p, FILE *fp){ |
66 | MyFileAccessorContext *pFileCtx = (MyFileAccessorContext *)p; |
67 | MyBufferContext *pBufCtx = pFileCtx->pBufCtx; |
68 | |
69 | if(pBufCtx->base.size != fread(pBufCtx->base.pBuf, 1, \ |
70 | pBufCtx->Base.size, fp)){ |
71 | file_error(pBufCtx->pAppCtx); |
72 | } |
73 | } |
74 | |
75 | /* 从pBuf写入文件中 */ |
76 | static void writer(FileAccessorContext *p, FILE *fp){ |
77 | MyFileAccessorContext *pFileCtx = (MyFileAccessorContext *)p; |
78 | MyBufferContext *pBufCtx = pFileCtx->pBufCtx; |
79 | |
80 | if(fwrite(pBufCtx->base.pBuf,1,pBufCtx->base.size, fp) != \ |
81 | pBufCtx->base.size){ |
82 | file_error(pBufCtx->pAppCtx); |
83 | } |
84 | } |
如上的处理流程中一共两次打开和关闭文件:首先打开文件计算大小,关闭,然后打开文件读取内容,关闭。为此还可以修改一下,将获取所需内存大小推迟至调用用户自定义函数后处理。内存分配函数指定大小的内存,并将其保存至上下文中返回给用户自定义函数处理。
1 | typedef struct BufferContext{ |
2 | void *pBuf; |
3 | size_t size; |
4 | bool (*processor)(struct BufferContext *p); |
5 | }BufferContext; |
6 | |
7 | bool buffer(BufferContext *pThis){ |
8 | assert(pThis); |
9 | /* 不在分配内存 */ |
10 | bool ret = pThis->processor(pThis); |
11 | free(pThis->pBuf); |
12 | return ret; |
13 | } |
14 | /* 内存分配函数 */ |
15 | void *allocate_buffer(BufferContext *pThis, size_t size){ |
16 | assert(pThis); |
17 | assert(pThis->pBuf == NULL); |
18 | |
19 | pThis->pBuf = malloc(size); |
20 | pThis->size = size; |
21 | return pThis->pBuf; |
22 | } |
23 | |
24 | |
25 | typedef struct FileAccessorContext{ |
26 | FILE *fp; /* 文件指针 */ |
27 | const char * const pFname; |
28 | const char * const pMode; |
29 | bool (* const processor)(struct FileAccessorContext *pThis) |
30 | }FileAccessorContext; |
31 | |
32 | |
33 | bool access_file(FileAccessorContext *pThis){ |
34 | assert(pThis); |
35 | /* 不在打开文件 */ |
36 | bool ret = pThis->processor(pThis); |
37 | if(pThis->fp != NULL){ |
38 | if(fclose(pThis->fp) != 0) |
39 | ret = false; |
40 | } |
41 | return ret; |
42 | } |
43 | |
44 | /* 打开文件函数 */ |
45 | FILE *get_file_pointer(FileAccessorContext *pThis){ |
46 | assert(pThis); |
47 | if(pThis->fp == NULL){ |
48 | pThis->fp = fopen(pThis->pFname, pThis->pMode); |
49 | } |
50 | return pThis->fp; |
51 | } |
52 | |
53 | /* context是应用程自身数据的上下文 */ |
54 | typedef struct{ |
55 | const char * const pFname; |
56 | int errorCategory; |
57 | }Context; |
58 | |
59 | IntSorterError int_sorter(const char *pFname){ |
60 | Context ctx = {pFname, ERROR_CAT_OK}; |
61 | |
62 | MyBufferContext bufCtx = {{NULL, 0, do_with_buffer}, &ctx}; |
63 | |
64 | buffer(&bufCtx.base); /* buffer函数内执行do_with_buffer函数 */ |
65 | |
66 | return ctx.errorCategory; |
67 | } |
68 | |
69 | static bool do_with_buffer(BufferContext *p){ |
70 | MyBufferContext *pBufCtx = (MyBufferContext *)p; |
71 | MyFileAccessorContext readFileCtx = \ |
72 | {{NULL,pBufCtx->pAppCtx->pFname,"rb",reader}, pBufCtx}; |
73 | |
74 | if(!access_file(&readFileCtx.base)){ /* 打开文件执行reader函数 */ |
75 | file_error(pBufCtx->pAppCtx); |
76 | return false; |
77 | } |
78 | |
79 | qsort(p->pBuf, p->size / sizeof(int), sizeof(int), comparator); |
80 | |
81 | MyFileAccessorContext writeFileCtx = \ |
82 | {{NULL,pBufCtx->pAppCtx->pFname,"wb",write}, pBufCtx}; |
83 | |
84 | if(!access_file(&writeFileCtx.base)){ /* 打开文件执行write函数 */ |
85 | file_error(pBufCtx->pAppCtx); |
86 | return false; |
87 | } |
88 | |
89 | return ture; |
90 | } |
91 | |
92 | static bool reader(FileAccessorContext *p){ |
93 | MyFileAccessorContext *pFileCtx = (MyFileAccessorContext *)p; |
94 | MyBufferContext *pBufCtx = pFileCtx->pBufCtx; |
95 | |
96 | long size = file_size(p); /* 求文件大小,内部调用size_reader函数 */ |
97 | if(size == -1){ |
98 | file_error(pBufCtx->pAppCtx); |
99 | return false; |
100 | } |
101 | |
102 | if(!allocate_buffer(&pBufCtx->base, size)){ /* 分配内存 */ |
103 | pBufCtx->pAppCtx->errorCategory = ERR_CAT_MEMORY; |
104 | return false; |
105 | } |
106 | |
107 | FILE *fp = get_file_pointer(p); |
108 | if(pBufCtx->base.size != fread(pBufCtx->base.pBuf, 1, \ |
109 | pBufCtx->base.size, fp)){ |
110 | file_error(pBufCtx->pAppCtx); |
111 | return false; |
112 | } |
113 | |
114 | return ture; |
115 | } |
116 | |
117 | static bool writer(FileAccessorContext *p){ |
118 | MyFileAccessorContext *pFileCtx = (MyFileAccessorContext *)p; |
119 | MyBufferContext *pBufCtx = pFileCtx->pBufCtx; |
120 | |
121 | FILE *fp = get_file_pointer(p); |
122 | if(fwrite(pBufCtx->base.pBuf, 1, pBufCtx->base.size, fp) != \ |
123 | pBufCtx->base.size){ |
124 | file_error(pBufCtx->pAppCtx); |
125 | return false; |
126 | } |
127 | return ture; |
128 | } |
129 | |
130 | static long file_size(FileAccessorContext *pThis){ |
131 | long save = file_current_pos(pThis); |
132 | if(save < 0) |
133 | return -1; |
134 | |
135 | if(set_file_pos(pThis, 0, SEEK_END) != 0) |
136 | return -1; |
137 | |
138 | long size = file_current_pos(pThis); |
139 | if(set_file_pos(pThis, save, SEEK_SET) != 0) |
140 | return -1; |
141 | |
142 | return size; |
143 | } |
144 | |
145 | static long file_current_pos(FileAccessorContext *pFileCtx){ |
146 | assert(pFileCtx); |
147 | FILE *fp = get_file_pointer(pFileCtx); |
148 | if(NULL == fp) |
149 | return -1; |
150 | |
151 | return ftell(fp); |
152 | } |
153 | |
154 | static int set_file_pos(FileAccessorContext *pFileCtx, long offset, \ |
155 | int whence){ |
156 | assert(pFileCtx); |
157 | FILE *fp = get_file_pointer(pFileCtx); |
158 | if(NULL == fp) |
159 | return -1; |
160 | |
161 | return fseek(fp, offset, whence); |
162 | } |
观察者模式
在模板方法模式例子中,如果指定一个不存在的文件int_sorter(“no_such_file”)时,其内部geit_file_pointer函数内部fopen函数调用失败。导致外部reader函数与do_with_buffer函数也会检测出错误,将错误信息打印出来,导致错误信息被输出了2次。
1 | struct FileAccessorContext; |
2 | |
3 | typedef struct FileErrorObserver{ |
4 | void (* const onError) (struct FileErrorObserver *pThis, \ |
5 | Struct FileAccessorContext *pFileCtx); |
6 | }FileErrorObserver; |
7 | |
8 | extern FileErrorObserver default_file_error_observer; |
9 | |
10 | typedef struct FileAccessorContext{ |
11 | FILE *fp; |
12 | const char *pFname; |
13 | const char *pMode; |
14 | bool (* const processor)(struct FileAccessorContext *pThis); |
15 | FileErrorObserver *pFileErrorObserver; |
16 | }FileAccessorContext; |
1 | FileErrorObserver default_file_error_observer = { |
2 | &default_file_error_handler |
3 | }; |
4 | |
5 | bool access_file(FileAccessorContext *pThis) { |
6 | assert(pThis); |
7 | if (pThis->pFileErrorObserver == NULL) |
8 | pThis->pFileErrorObserver = &default_file_error_observer; |
9 | bool ret = pThis->processor(pThis); |
10 | if (pThis->fp != NULL) { |
11 | if (fclose(pThis->fp) != 0) { |
12 | pThis->pFileErrorObserver->onError( \ |
13 | pThis->pFileErrorObserver, pThis); |
14 | ret = false; |
15 | } |
16 | } |
17 | |
18 | return ret; |
19 | } |
20 | |
21 | FILE *get_file_pointer(FileAccessorContext *pThis) { |
22 | assert(pThis); |
23 | if (pThis->fp == NULL) { |
24 | pThis->fp = fopen(pThis->pFname, pThis->pMode); |
25 | if (pThis->fp == NULL) |
26 | pThis->pFileErrorObserver->onError( \ |
27 | pThis->pFileErrorObserver, pThis); |
28 | } |
29 | |
30 | return pThis->fp; |
31 | } |
32 | |
33 | static void default_file_error_handler(FileErrorObserver *pThis \ |
34 | FileAccessorContext *pFileCtx) { |
35 | fprintf(stderr, "File access error '%s'(mode: %s): %s\n", \ |
36 | pFileCtx->pFname, pFileCtx->pMode, strerror(errno)); |
37 | } |
1 | long file_size(FileAccessorContext *pFileCtx) { |
2 | long save = file_current_pos(pFileCtx); |
3 | if (save < 0) return -1; |
4 | |
5 | if (set_file_pos(pFileCtx, 0, SEEK_END) != 0) return -1; |
6 | |
7 | long size = file_current_pos(pFileCtx); |
8 | set_file_pos(pFileCtx, save, SEEK_SET); |
9 | |
10 | return size; |
11 | } |
12 | |
13 | long file_current_pos(FileAccessorContext *pThis) { |
14 | assert(pThis); |
15 | FILE *fp = get_file_pointer(pThis); |
16 | if (fp == NULL) return -1; |
17 | |
18 | long ret = ftell(fp); |
19 | if (ret < 0) pThis->pFileErrorObserver->onError( \ |
20 | pThis->pFileErrorObserver, pThis); |
21 | return ret; |
22 | } |
23 | |
24 | int set_file_pos(FileAccessorContext *pThis, long offset, int whence) { |
25 | assert(pThis); |
26 | FILE *fp = get_file_pointer(pThis); |
27 | if (fp == NULL) return -1; |
28 | |
29 | int ret = fseek(fp, offset, whence); |
30 | if (ret != 0) pThis->pFileErrorObserver->onError( \ |
31 | pThis->pFileErrorObserver, pThis); |
32 | return ret; |
33 | } |
34 | |
35 | bool read_file(FileAccessorContext *pThis, BufferContext *pBufCtx) { |
36 | FILE *fp = get_file_pointer(pThis); |
37 | if (fp == NULL) return false; |
38 | |
39 | if (pBufCtx->size != fread(pBufCtx->pBuf, 1, pBufCtx->size, fp)) { |
40 | pThis->pFileErrorObserver->onError(pThis->pFileErrorObserver, \ |
41 | pThis); |
42 | return false; |
43 | } |
44 | return true; |
45 | } |
46 | |
47 | bool write_file(FileAccessorContext *pThis, BufferContext *pBufCtx) { |
48 | FILE *fp = get_file_pointer(pThis); |
49 | if (fp == NULL) return false; |
50 | |
51 | if (pBufCtx->size != fwrite(pBufCtx->pBuf, 1, pBufCtx->size, fp)) { |
52 | pThis->pFileErrorObserver->onError(pThis->pFileErrorObserver, \ |
53 | pThis); |
54 | return false; |
55 | } |
56 | return true; |
57 | } |
1 | static bool reader(FileAccessorContext *pFileCtx); |
2 | static bool do_with_buffer(BufferContext *pBufCtx); |
3 | static bool writer(FileAccessorContext *pFileCtx); |
4 | static int comparator(const void *p1, const void *p2); |
5 | static void file_error(FileErrorObserver *pThis, \ |
6 | FileAccessorContext *pFileCtx); |
7 | |
8 | typedef struct { |
9 | BufferContext base; |
10 | Context *pAppCtx; |
11 | } MyBufferContext; |
12 | |
13 | typedef struct { |
14 | FileAccessorContext base; |
15 | MyBufferContext *pBufCtx; |
16 | } MyFileAccessorContext; |
17 | |
18 | typedef struct { |
19 | FileAccessorContext base; |
20 | long size; |
21 | } SizeGetterContext; |
22 | |
23 | static FileErrorObserver file_error_observer = { |
24 | file_error |
25 | }; |
26 | |
27 | IntSorterError int_sorter(const char *pFname) { |
28 | Context ctx = {pFname, ERR_CAT_OK}; |
29 | |
30 | MyBufferContext bufCtx = {{NULL, 0, do_with_buffer}, &ctx}; |
31 | buffer(&bufCtx.base); |
32 | return ctx.errorCategory; |
33 | } |
34 | |
35 | static bool do_with_buffer(BufferContext *p) { |
36 | MyBufferContext *pBufCtx = (MyBufferContext *)p; |
37 | MyFileAccessorContext readFileCtx = { |
38 | {NULL, pBufCtx->pAppCtx->pFname, "rb", reader, \ |
39 | &file_error_observer}, pBufCtx }; |
40 | |
41 | if (! access_file(&readFileCtx.base)) return false; |
42 | |
43 | qsort(p->pBuf, p->size / sizeof(int), sizeof(int), comparator); |
44 | |
45 | MyFileAccessorContext writeFileCtx = { |
46 | {NULL, pBufCtx->pAppCtx->pFname, "wb", writer, \ |
47 | &file_error_observer},pBufCtx}; |
48 | return access_file(&writeFileCtx.base); |
49 | } |
50 | |
51 | static bool reader(FileAccessorContext *p) { |
52 | MyFileAccessorContext *pFileCtx = (MyFileAccessorContext *)p; |
53 | |
54 | long size = file_size(p); |
55 | if (size == -1) return false; |
56 | |
57 | if (! allocate_buffer(&pFileCtx->pBufCtx->base, size)) { |
58 | pFileCtx->pBufCtx->pAppCtx->errorCategory = ERR_CAT_MEMORY; |
59 | return false; |
60 | } |
61 | |
62 | return read_file(p, &pFileCtx->pBufCtx->base); |
63 | } |
64 | |
65 | static bool writer(FileAccessorContext *p) { |
66 | MyFileAccessorContext *pFileCtx = (MyFileAccessorContext *)p; |
67 | return write_file(p, &pFileCtx->pBufCtx->base); |
68 | } |
69 | |
70 | static int comparator(const void *p1, const void *p2) { |
71 | int i1 = *(const int *)p1; |
72 | int i2 = *(const int *)p2; |
73 | if (i1 < i2) return -1; |
74 | if (i1 > i2) return 1; |
75 | return 0; |
76 | } |
77 | |
78 | static void file_error(FileErrorObserver *pThis, \ |
79 | FileAccessorContext *pFileCtx) { |
80 | |
81 | default_file_error_observer.onError(pThis, pFileCtx); |
82 | |
83 | MyFileAccessorContext *pMyFileCtx = (MyFileAccessorContext *)pFileCtx; |
84 | pMyFileCtx->pBufCtx->pAppCtx->errorCategory = ERR_CAT_FILE; |
85 | } |
观察者模式基本上会通过使用回调来切断监控方与被监控对象之间的依赖关系。
动态增加观察者的观察模式
1 | /* 可变长数组 */ |
2 | typedef struct ArrayList { |
3 | const int capacity; |
4 | void ** const pBuf; |
5 | size_t index; |
6 | |
7 | struct ArrayList *(* const add)(struct ArrayList *pThis, void *pData); |
8 | void *(* const remove)(struct ArrayList *pThis, void *pData); |
9 | void *(* const get)(struct ArrayList *pThis, int index); |
10 | size_t (* const size)(struct ArrayList *pThis); |
11 | } ArrayList; |
12 | |
13 | ArrayList *add_to_array_list(ArrayList *pThis, void *pData); |
14 | void *remove_from_array_list(ArrayList *pThis, void *pData); |
15 | void *get_from_array_list(ArrayList *pThis, int index); |
16 | size_t array_list_size(ArrayList *pThis); |
17 | |
18 |
|
19 | {sizeof(array) / sizeof(void *), array, \ |
20 | 0, add_to_array_list, remove_from_array_list, \ |
21 | get_from_array_list, array_list_size} |
22 | |
23 | /* 在数组末尾增加数据并返回数组自身,当数组容量不足时返回断言错误 */ |
24 | ArrayList *add_to_array_list(ArrayList *pThis, void *pData) { |
25 | assert(pThis->capacity > pThis->index); |
26 | pThis->pBuf[pThis->index++] = pData; |
27 | return pThis; |
28 | } |
29 | |
30 | /* 删除指定的数据并返回删除的数据,如果没找到要删除的数据则返回NULL */ |
31 | void *remove_from_array_list(ArrayList *pThis, void *pData) { |
32 | int i; |
33 | |
34 | for (i = 0; i < pThis->index; ++i) { |
35 | if (pThis->pBuf[i] == pData) { |
36 | memmove(pThis->pBuf + i, pThis->pBuf + i + 1, \ |
37 | (pThis->index - i - 1) * sizeof(void *)); |
38 | --pThis->index; |
39 | return pData; |
40 | } |
41 | } |
42 | |
43 | return NULL; |
44 | } |
45 | |
46 | /* 返回指定索引位置的数据 */ |
47 | void *get_from_array_list(ArrayList *pThis, int index) { |
48 | assert(0 <= index && pThis->index > index); |
49 | return pThis->pBuf[index]; |
50 | } |
51 | |
52 | /* 返回数组中保存的数据的个数 */ |
53 | size_t array_list_size(ArrayList *pThis) { |
54 | return pThis->index; |
55 | } |
1 | struct FileAccessorContext; |
2 | |
3 | typedef struct FileErrorObserver { |
4 | void (* const onError)(struct FileErrorObserver *pThis, \ |
5 | struct FileAccessorContext *pFileCtx); |
6 | } FileErrorObserver; |
7 | |
8 | extern FileErrorObserver default_file_error_observer; |
9 | |
10 | typedef struct FileAccessorContext { |
11 | FILE *fp; |
12 | const char *pFname; |
13 | const char *pMode; |
14 | ArrayList observer_table; /* ArrayList里动态存储多个FileErrorObserver对象 */ |
15 | |
16 | bool (* const processor)(struct FileAccessorContext *pThis); |
17 | } FileAccessorContext; |
1 | bool access_file(FileAccessorContext *pThis) { |
2 | assert(pThis); |
3 | bool ret = pThis->processor(pThis); |
4 | if (pThis->fp != NULL) { |
5 | if (fclose(pThis->fp) != 0) { |
6 | fire_error(pThis); |
7 | ret = false; |
8 | } |
9 | } |
10 | |
11 | return ret; |
12 | } |
13 | |
14 | static void fire_error(FileAccessorContext *pThis) { |
15 | ArrayList *pTbl = &pThis->observer_table; |
16 | int i; |
17 | for (i = 0; i < pTbl->index; ++i) { |
18 | FileErrorObserver *pObserver = pTbl->get(pTbl, i); |
19 | pObserver->onError(pObserver, pThis); |
20 | } |
21 | } |
22 | |
23 | void add_file_error_observer(FileAccessorContext *pThis, \ |
24 | FileErrorObserver *pErrorObserver) { |
25 | ArrayList *pTable = &pThis->observer_table; |
26 | pTable->add(pTable, pErrorObserver); |
27 | } |
28 | |
29 | void remove_file_error_observer(FileAccessorContext *pThis, \ |
30 | FileErrorObserver *pErrorObserver) { |
31 | ArrayList *pTable = &pThis->observer_table; |
32 | pTable->remove(pTable, pErrorObserver); |
33 | } |
责任链模式
1 |
|
2 |
|
3 | |
4 |
|
5 | |
6 |
|
7 | extern "C" { |
8 |
|
9 | |
10 | typedef struct Validator { |
11 | bool (* const validate)(struct Validator *pThis, int val); |
12 | } Validator; |
13 | |
14 | typedef struct { |
15 | Validator base; |
16 | const int min; |
17 | const int max; |
18 | } RangeValidator; |
19 | |
20 | typedef struct { |
21 | Validator base; |
22 | int previousValue; |
23 | } PreviousValueValidator; |
24 | |
25 | /* 校验器责任链 */ |
26 | typedef struct ChainedValidator { |
27 | Validator base; |
28 | Validator *pWrapped; |
29 | Validator *pNext; |
30 | } ChainedValidator; |
31 | |
32 | typedef struct { |
33 | int top; |
34 | const size_t size; |
35 | int * const pBuf; |
36 | Validator * const pValidator; |
37 | } Stack; |
38 | |
39 | bool validateRange(Validator *pThis, int val); |
40 | bool validatePrevious(Validator *pThis, int val); |
41 | bool validateChain(Validator *pThis, int val); |
42 | |
43 |
|
44 | {{validateRange}, (min), (max)} |
45 | |
46 |
|
47 | {{validatePrevious}, 0} |
48 | |
49 |
|
50 | {{validateChain}, (wrapped), (next)} |
51 | |
52 | bool push(Stack *p, int val); |
53 | bool pop(Stack *p, int *pRet); |
54 | |
55 |
|
56 | 0, sizeof(buf) / sizeof(int), (buf), \ |
57 | NULL \ |
58 | } |
59 | |
60 |
|
61 | 0, sizeof(buf) / sizeof(int), (buf), \ |
62 | pValidator \ |
63 | } |
64 | |
65 |
|
66 | } |
67 |
|
68 | |
69 |
|
1 |
|
2 |
|
3 | |
4 | static bool isStackFull(const Stack *p) { |
5 | return p->top == p->size; |
6 | } |
7 | |
8 | static bool isStackEmpty(const Stack *p) { |
9 | return p->top == 0; |
10 | } |
11 | |
12 | bool validateRange(Validator *p, int val) { |
13 | RangeValidator *pThis = (RangeValidator *)p; |
14 | return pThis->min <= val && val <= pThis->max; |
15 | } |
16 | |
17 | bool validatePrevious(Validator *p, int val) { |
18 | PreviousValueValidator *pThis = (PreviousValueValidator *)p; |
19 | if (val < pThis->previousValue) return false; |
20 | pThis->previousValue = val; |
21 | return true; |
22 | } |
23 | |
24 | bool validate(Validator *p, int val) { |
25 | if (! p) return true; |
26 | return p->validate(p, val); |
27 | } |
28 | |
29 | // true: 成功, false: 失敗 |
30 | bool push(Stack *p, int val) { |
31 | if (! validate(p->pValidator, val) || isStackFull(p)) return false; |
32 | p->pBuf[p->top++] = val; |
33 | return true; |
34 | } |
35 | |
36 | // true: 成功, false: 失敗 |
37 | bool pop(Stack *p, int *pRet) { |
38 | if (isStackEmpty(p)) return false; |
39 | *pRet = p->pBuf[--p->top]; |
40 | return true; |
41 | } |
42 | |
43 | /* 链式校验 */ |
44 | bool validateChain(Validator *p, int val) { |
45 | ChainedValidator *pThis = (ChainedValidator *)p; |
46 | |
47 | p = pThis->pWrapped; |
48 | if (! p->validate(p, val)) return false; |
49 | |
50 | p = pThis->pNext; |
51 | return !p || p->validate(p, val); |
52 | } |
访问者模式
1 | struct ValidatorVisitor; |
2 | |
3 | typedef struct Validator { |
4 | bool (* const validate)(struct Validator *pThis, int val); |
5 | void (* const accept)(struct Validator *pThis, \ |
6 | struct ValidatorVisitor *pVisitor); |
7 | } Validator; |
8 | |
9 | typedef struct { |
10 | Validator base; |
11 | const int min; |
12 | const int max; |
13 | } RangeValidator; |
14 | |
15 | typedef struct { |
16 | Validator base; |
17 | int previousValue; |
18 | } PreviousValueValidator; |
19 | |
20 | typedef struct ValidatorVisitor { |
21 | void (* const visitRange)(struct ValidatorVisitor *pThis, \ |
22 | RangeValidator *p); |
23 | void (* const visitPreviousValue)(struct ValidatorVisitor *pThis, \ |
24 | PreviousValueValidator *p); |
25 | } ValidatorVisitor; |
26 | |
27 | void acceptRange(Validator *pThis, ValidatorVisitor *pVisitor); |
28 | void acceptPrevious(Validator *pThis, ValidatorVisitor *pVisitor); |
29 | |
30 | bool validateRange(Validator *pThis, int val); |
31 | bool validatePrevious(Validator *pThis, int val); |
32 | |
33 |
|
34 | {{validateRange, acceptRange}, (min), (max)} |
35 | |
36 |
|
37 | {{validatePrevious, acceptPrevious}, 0} |
38 | ` |
1 | bool validateRange(Validator *p, int val) { |
2 | RangeValidator *pThis = (RangeValidator *)p; |
3 | return pThis->min <= val && val <= pThis->max; |
4 | } |
5 | |
6 | bool validatePrevious(Validator *p, int val) { |
7 | PreviousValueValidator *pThis = (PreviousValueValidator *)p; |
8 | if (val < pThis->previousValue) return false; |
9 | pThis->previousValue = val; |
10 | return true; |
11 | } |
12 | |
13 | void acceptRange(Validator *p, ValidatorVisitor *pVisitor) { |
14 | pVisitor->visitRange(pVisitor, (RangeValidator *)p); |
15 | } |
16 | |
17 | void acceptPrevious(Validator *p, ValidatorVisitor *pVisitor) { |
18 | pVisitor->visitPreviousValue(pVisitor, (PreviousValueValidator *)p); |
19 | } |
1 | static void rangeView(ValidatorVisitor *pThis, RangeValidator *p); |
2 | static void previousValueView(ValidatorVisitor *pThis, \ |
3 | PreviousValueValidator *p); |
4 | |
5 | typedef struct ViewVisitor { |
6 | ValidatorVisitor base; |
7 | char *pBuf; |
8 | size_t size; |
9 | } ViewVisitor; |
10 | |
11 | void printValidator(Validator *p, char *pBuf, size_t size) { |
12 | ViewVisitor visitor = {{rangeView, previousValueView}, pBuf, size}; |
13 | p->accept(p, &visitor.base); |
14 | } |
15 | |
16 | static void rangeView(ValidatorVisitor *pThis, RangeValidator *p) { |
17 | ViewVisitor *pVisitor = (ViewVisitor* )pThis; |
18 | snprintf(pVisitor->pBuf, pVisitor->size, \ |
19 | "Range(%d-%d)", p->min, p->max); |
20 | } |
21 | |
22 | static void previousValueView(ValidatorVisitor *pThis, \ |
23 | PreviousValueValidator *p) { |
24 | ViewVisitor *pVisitor = (ViewVisitor* )pThis; |
25 | snprintf(pVisitor->pBuf, pVisitor->size, "Previous"); |
26 | } |