有些話說
頭文件可以作為接口定義,再加上static修飾符,就很容易定義私有的接口,每一個(gè)具體的實(shí)現(xiàn),即所有包含所有私有接口的頭文件,都必須要完整實(shí)現(xiàn)所有已聲明但未實(shí)現(xiàn)的函數(shù),否則gcc編譯不過去。廢話不多說,進(jìn)入步驟吧。
開始實(shí)施
以毫無用處的blog為例,簡(jiǎn)單兩個(gè)方法就行了,需要每一個(gè)實(shí)現(xiàn)暴露一個(gè)可以外部調(diào)用函數(shù)。
定義一個(gè)結(jié)構(gòu),公用
blog.h:
#ifndef _BLOG_H
#define _BLOG_H
typedef struct {
char *name;
void (*init)(void);
void (*welcome)(void);
} blog_t;
#endif
這個(gè)頭文件定義了一個(gè)對(duì)象。可以自由的被包含,包含之后自由使用blog_t結(jié)構(gòu)。
定義一個(gè)完全私有函數(shù)頭文件
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
這個(gè)文件聲明定義了若干static屬性的函數(shù),因此,只要包含此頭文件的C源文件,必須實(shí)現(xiàn)welcome與init函數(shù),否則gcc編譯不通過。需要注意此頭文件私有。
編寫實(shí)現(xiàn)
blog1.c,僅僅一個(gè)實(shí)現(xiàn):
#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;
}
僅有一個(gè)對(duì)外入口:gen_blog1_ptr
,也就是此實(shí)現(xiàn)對(duì)外唯一的交互方式。
blog2.c,默認(rèn)的實(shí)現(xiàn):
#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;
}
此文件對(duì)外唯一入口為:gen_blog2_ptr
。
其實(shí)兩個(gè)實(shí)現(xiàn)已經(jīng)可以了,但多一個(gè)說明接口單一,實(shí)現(xiàn)多樣性,再說錦上添花也是人們喜歡做的事情。
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;
}
一樣可以看到類似約定好的對(duì)外函數(shù)名稱gen_blog3_ptr
應(yīng)用端實(shí)現(xiàn)
我們以app.c作為應(yīng)用入口:
#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;
} 這里分別調(diào)用blog1.c, blog2.c, blog3.c,唯一入口,執(zhí)行簡(jiǎn)單的邏輯。
編譯運(yùn)行
編譯命令行代碼很簡(jiǎn)單:
gcc -o app app.c blog1.c blog2.c blog3.c
運(yùn)行:
./app
運(yùn)行效果:
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!
小結(jié)
這里借助兩個(gè)頭文件,模擬了私有接口,公有結(jié)構(gòu)體對(duì)象,三個(gè)具體子類實(shí)現(xiàn)。
在c_socket.io_server
項(xiàng)目中,作用于具體的實(shí)現(xiàn),以及定義了傳輸通道模型和實(shí)現(xiàn),互相不干擾。
當(dāng)然和JAVA相比,模擬對(duì)象程度稍低了一些,但夠用了。這個(gè)世界不僅僅只有面向?qū)ο螅€有面向并發(fā)的函數(shù)式Erlang,還有面向軟件工程的大型語言Go。嗯,面向?qū)ο蟛贿^是這個(gè)世界其中一角,天生存在缺陷,也不是被所有人喜歡。組件公用、庫(kù)的概念,倒是大部分語言都很自然的欣然接受。面向過程,面向?qū)ο螅贿^是大部分人給與的標(biāo)簽,怎么用才重要。