依賴注入(Dependency Injection 簡(jiǎn)寫為DI)開發(fā)過(guò)程中解除耦合的經(jīng)典手段,但是似乎從一開始這貨就是為面向?qū)ο蠖模宜吹降氖纠紱](méi)有將C語(yǔ)言考慮在內(nèi)。難道C語(yǔ)言不能使用這么經(jīng)典的設(shè)計(jì)模式?本文就來(lái)介紹一下C語(yǔ)言如實(shí)使用依賴注入來(lái)解除耦合。 參數(shù)注入 對(duì)應(yīng)于面向?qū)ο笳Z(yǔ)言的構(gòu)造函數(shù)注入,C語(yǔ)言作為過(guò)程語(yǔ)言,參數(shù)注入法是最簡(jiǎn)單、也是最直接的方法。最常見的排序方法qsort就是用這種方法: void qsort(void* base, size_t num, size_t size, int (*compar)(const void*,const void*)); 可以看到qsort函數(shù)的第四個(gè)參數(shù)compar就是外部依賴的對(duì)象(函數(shù)),因?yàn)椴煌瑘?chǎng)景有不同的比較元素大小的方式,通過(guò)參數(shù)將外部依賴注入,使該函數(shù)更加具有通用型,因?yàn)閷?shí)際上我們用qsort,只是用他的排序算法,其他的都是和具體使用場(chǎng)景有關(guān)。 設(shè)置(set)接口注入 上一篇我們介紹的設(shè)置回調(diào)函數(shù)的方法其實(shí)就是使用這種方法,其本質(zhì)就是專門對(duì)外提供一個(gè)接口,用來(lái)將依賴的外部對(duì)象或者函數(shù)注入到本模塊中來(lái)。比如開發(fā)一個(gè)模塊,需要申請(qǐng)內(nèi)存,但是為了易用性,除了使用系統(tǒng)自帶的內(nèi)存申請(qǐng)函數(shù),我們需要支持第三方的內(nèi)存池模塊來(lái)申請(qǐng)內(nèi)存,我們就可以提供一個(gè)API來(lái)設(shè)置申請(qǐng)和釋放內(nèi)存的函數(shù),如下示例: ///默認(rèn)申請(qǐng)內(nèi)存方式為系統(tǒng)自帶的函數(shù)static void *(*malloc_function)(size_t size) = malloc;static void (*free_function)(void *p) = free;int sample_module_init(){ return 0; }///設(shè)置新的分配內(nèi)存的函數(shù)int sample_module_set_memory_api(void *(*get)(size_t size), void(*put)(void *p)){ malloc_function = get; free_function = put; return 0;}///申請(qǐng)一個(gè)size大小的int類型數(shù)組int *sample_module_create_int_array(size_t *size){ int *p = (int *)malloc_function(sizeof(int) * size); return p;} 這樣我們?cè)谑褂眠@個(gè)模塊時(shí),就可以設(shè)置三方的內(nèi)存申請(qǐng)方式了。比如想使用jemalloc的內(nèi)存分配方式,調(diào)用sample_module_set_memory_api 將內(nèi)存分配的函數(shù)指針設(shè)置為該庫(kù)的內(nèi)存申請(qǐng)API就可以了。 sample_module_set_memory_api(mallocx, freex);int *p = sample_module_create_int_array(2); 基于Interface的注入 面向?qū)ο缶幊逃幸粋€(gè)接口(Interface)的概念。從概念上講,接口是一個(gè)抽象類,代表一系列行為相同的類;從實(shí)現(xiàn)上來(lái)講,接口就是一堆方法的集合。C語(yǔ)言雖然沒(méi)有接口的的概念,但是完全可以實(shí)現(xiàn)接口的功能,通過(guò)結(jié)構(gòu)體將一系列函數(shù)指針組合起來(lái),就可以實(shí)現(xiàn)接口的功能。比如我們需要實(shí)現(xiàn)一個(gè)緩存功能模塊,包括set_value和get_value兩個(gè)方法,為了使模塊更具有擴(kuò)展性,我們先定義抽象接口 struct cache_interface{ ///store kv in cache int (*set_value)(void *instance, const char *key, const char *value); ///find kv in cache int (*get_value)(void *instance, const char *key, char **value_out);}; 在面向?qū)ο笳Z(yǔ)言中,接口不能實(shí)例化。C語(yǔ)言中雖然這個(gè)結(jié)構(gòu)體可以實(shí)例化,但是實(shí)例化后沒(méi)有任何意義,其中的函數(shù)指針仍然無(wú)值可賦,所以我們要在另外的文件中實(shí)現(xiàn)這個(gè)接口: ///實(shí)現(xiàn)方式---本地文件緩存struct cache_local_file{ ///必須是第一個(gè)成員 struct cache_interface methods; char file_path[32]; FILE *fp;};int set_value(struct cache_interface *instance, const char *key, const char *value){ struct cache_local_file *ins = (struct cache_local_file *)instance; fprintf(ins->fp, '%s:%s', key, value); return 0;}int get_value(struct cache_interface *instance, const char *key, const char **value_out){ *value_out = 'sample data'; return 0;}//創(chuàng)建實(shí)例(創(chuàng)建一個(gè)實(shí)例,相當(dāng)于構(gòu)造函數(shù))int cahce_local_file_create(const char *path, struct cache_interface **instance){ struct cache_local_file *ins = (struct cache_local_file *)malloc(sizeof(cache_local_file)); strncpy(ins->file_path, sizeof(ins->file_path), path); ins->fp = fopen(path); ins->methods.get_value = get_value; ins->methods.set_value = set_value; *instance = &(ins->methods); return 0;} 上面實(shí)現(xiàn)一個(gè)利用本地文件存儲(chǔ)數(shù)據(jù)的緩存方法,為了更變的使用這些實(shí)現(xiàn),我們需要提供統(tǒng)一的API來(lái)共用戶使用,這樣當(dāng)緩存的具體實(shí)現(xiàn)有變化時(shí),使用緩存的用戶不用大規(guī)模修改代碼,甚至不用修改代碼。下面我們類似于面向?qū)ο罄锏?em>工廠模式來(lái)提供這些API。 struct cache_implement{ char name[32], int (*create)(const char *input, struct cache_interface **instance);}struct cache_implement impl[32] = {0};void init(){ strncpy(impl[0].name, sizeof(impl[0].name), 'local_file'); impl[0].create = cahce_local_file_create; ///more implement to add};int cache_create(const char *type, const char *param, struct cache_interface **ins){ for(int i =0 ; i < 32; i++) { if (0 == strcmp(type, impl[i].name)) { impl[i].create(type, param, ins); } } return 0;}int cache_set_value(struct cache_interface *ins, const char *key, const char *value){ ///another user code ///set kv return ins->set_value(ins, key, value);}int cache_get_value(struct cache_interface *ins, const char *key, const cahr **value_out){ ///another user code; ///get kv return ins->get_value(ins, key, value);} 這樣的話,使用者就只關(guān)注到三個(gè)API,如果要變換不同的緩存實(shí)現(xiàn)(比如使用redis或者memcache存儲(chǔ)數(shù)據(jù)),只要修改cache_create的type參數(shù)即可。 總結(jié):
|
|