有些话说
头文件可以作为接口定义,再加上static修饰符,就很容易定义私有的接口,每一个具体的实现,即所有包含所有私有接口的头文件,都必须要完整实现所有已声明但未实现的函数,否则gcc编译不过去。废话不多说,进入步骤吧。
开始实施
以毫无用处的blog为例,简单两个方法就行了,需要每一个实现暴露一个可以外部调用函数。
定义一个结构,公用
blog.h:
#ifndef _BLOG_H
#define _BLOG_H
typedef struct {
char *name;
void (*init)(void);
void (*welcome)(void);
} blog_t;
#endif
这个头文件定义了一个对象。可以自由的被包含,包含之后自由使用blog_t结构。
定义一个完全私有函数头文件
blog_impl.h:
#ifndef _BLOG_IMPL_H
#define _BLOG_IMPL_H
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "blog.h" //继承blog.h文件
static void welcome(void);
static void init(void);
static blog_t *gen_default(char *name) {
blog_t *blog = (blog_t *)malloc(sizeof(blog_t));
blog->name = strdup(name);
blog->init = init;
blog->welcome = welcome;
return blog;
}
#endif
这个文件声明定义了若干static属性的函数,因此,只要包含此头文件的C源文件,必须实现welcome与init函数,否则gcc编译不通过。需要注意此头文件私有。
编写实现
blog1.c,仅仅一个实现:
#include "blog_impl.h"
static blog_t *this;
static void init(void){
printf("the blog owner is %s\n", this->name);
}
static void welcome(void){
printf("here is the %s haha !\n", this->name);
}
blog_t *gen_blog1_ptr(){
blog_t *blog = gen_default("blog1");
this = blog;
return blog;
}
仅有一个对外入口:gen_blog1_ptr
,也就是此实现对外唯一的交互方式。
blog2.c,默认的实现:
#include "blog_impl.h"
static void init(void){
printf("Here is the default blog init action !\n");
}
static void welcome(void){
printf("The system's welcome action !\n");
}
blog_t *gen_blog2_ptr(){
blog_t *blog = gen_default("default");
return blog;
}
此文件对外唯一入口为:gen_blog2_ptr
。
其实两个实现已经可以了,但多一个说明接口单一,实现多样性,再说锦上添花也是人们喜欢做的事情。
blog3.c,添花版:
#include "blog_impl.h"
static blog_t *this;
static void init(void){
printf("Hi, %s\n", this->name);
}
static void welcome(void){
printf("you are welcome %s!\n", this->name);
}
blog_t *gen_blog3_ptr(){
blog_t *blog = gen_default("blog3");
this = blog;
return blog;
}
一样可以看到类似约定好的对外函数名称gen_blog3_ptr
应用端实现
我们以app.c作为应用入口:
#include <stdio.h>
#include <stdlib.h>
#include "blog.h"
int main(int argc, char const *argv[]) {
blog_t *blog1 = gen_blog1_ptr();
blog_t *blog2 = gen_blog2_ptr();
blog_t *blog3 = gen_blog3_ptr();
printf("the blog1's actions \n");
blog1->init();
blog1->welcome();
printf("\n");
printf("the blog2's actions \n");
blog2->init();
blog2->welcome();
printf("\n");
printf("the blog3's actions \n");
blog3->init();
blog3->welcome();
printf("\n");
return 0;
} 这里分别调用blog1.c, blog2.c, blog3.c,唯一入口,执行简单的逻辑。
编译运行
编译命令行代码很简单:
gcc -o app app.c blog1.c blog2.c blog3.c
运行:
./app
运行效果:
the blog1's actions ...
the blog owner is blog1
here is the blog1 haha !
the blog2's actions ...
Here is the default blog init action !
The system's welcome action !
the blog3's actions ...
Hi, blog3
you are welcome blog3!
小结
这里借助两个头文件,模拟了私有接口,公有结构体对象,三个具体子类实现。
在c_socket.io_server
项目中,作用于具体的实现,以及定义了传输通道模型和实现,互相不干扰。
当然和JAVA相比,模拟对象程度稍低了一些,但够用了。这个世界不仅仅只有面向对象,还有面向并发的函数式Erlang,还有面向软件工程的大型语言Go。嗯,面向对象不过是这个世界其中一角,天生存在缺陷,也不是被所有人喜欢。组件公用、库的概念,倒是大部分语言都很自然的欣然接受。面向过程,面向对象,不过是大部分人给与的标签,怎么用才重要。