面向對象發展到今天,已經出現了許許多多優秀的實踐、方法和技術。不少的技術都可以有效的提升軟件質量。IBM上的《面向對象軟件開發和過程》系列文章對面對對象設計從以下層面進行了詳細的介紹:代碼是核心、 案例實戰(上)、 案例實戰(下)、 重用、優化代碼的組織、 針對契約設計、 業務建模。html
雖然文章中的案例實戰或代碼示例都是以Java、C++等面對對象語言爲主,可是輔以必定的方法和工具,C語言同樣能作出優秀的面對對象設計,由於軟件設計的基本理念原則是相通的。C語言面對對象設計的主要難點在於,對於抽象、繼承、泛型(模板)等面對對象設計手段,在語法層次上缺乏直接的支持。linux
本文試圖對以下四個主要的面對對象設計手段提供C語言的解決方案和示例。數據結構
要點:
a. 頭文件只提供類型聲明和接口聲明
b. 類型定義和接口實如今.c中完成
c. 接口支持參數類型檢查app
/* stach.h */ #ifndef STACK_H #define STACK_H typedef struct stack *stack; extern stack new(void); extern void free(stack *stk); extern int empty(stack stk); extern void push(stack stk, void *x); extern void *pop(stack stk);
要點:
a. 頭文件只提供接口聲明
b. 定義一個 void * 指針類型全局變量,封裝接口須要的全部和對象相關的信息
c. 類型定義和接口實如今.c中完成
d. 接口不支持參數類型檢查函數
/* set.h */ #ifndef H #define H //Set, Object 本質是對象size的指針,用於new一個新對象 extern const void * Set; extern const void * Object; extern void * new (const void * type, ...); extern void delete (void * item); extern void * add (void * set, const void * element); extern void * find (const void * set, const void * element); extern void * drop (void * set, const void * element); extern int contains (const void * set, const void * element); extern unsigned count (const void * set); #endif /* main.c */ int main () { void * s = new(Set); void * a = add(s, new(Object)); void * b = add(s, new(Object)); void * c = new(Object); if (contains(s, a) && contains(s, b)) puts("ok"); if (contains(s, c)) puts("contains?"); if (differ(a, add(s, a))) puts("differ?"); if (contains(s, drop(s, a))) puts("drop?"); delete(drop(s, b)); delete(drop(s, c)); return 0; }
要點:
a. 純虛基類以聚合(關聯)方式繼承,類型爲const void *以便於信息隱藏
b. 非純虛基類以組合方式繼承,類型爲const struct superclass_name
c. 全部基類必須做爲派生類第一個成員
d. 基類在派生類中以命名爲'_'進行信息隱藏
e. 純虛基類在各個派生類中實例化
f. 對外部不暴露具體數據類型工具
extern const void * Circle; /* new(Circle, x, y, rad) */ extern const void * Point; /* new(Point, x, y); */ void * new (const void * class, ...); void delete (void * item); void draw (const void * self); void move (void * point, int dx, int dy); /* .c */ struct Class { size_t size; void * (* ctor) (void * self, va_list * app); void * (* dtor) (void * self); void (* draw) (const void * self); }; struct Point { const void * class; int x, y; }; struct Circle { const struct Point _; int rad; }; void move (void * _self, int dx, int dy) { struct Point * self = _self; self -> x += dx, self -> y += dy; } void draw (const void * self) { const struct Class * const * cp = self; assert(self && * cp && (* cp) -> draw); (* cp) -> draw(self); }
要點:
a. 基類做爲派生類的一個字段,但沒必要做爲派生類的第一個字段
b. 經過 container_of 方法找到派生類的基類
c. 對外不暴露派生類類型,但暴露基類類型
d. 支持多重繼承
e. 多用於業務邏輯/流程在派生類模塊中實現的場景,基類爲派生類提供公共服務post
struct ep_device { struct usb_endpoint_descriptor *desc; struct usb_device *udev; struct device dev; }; #define to_ep_device(_dev) \ container_of(_dev, struct ep_device, dev)
要點:
a. 派生類做爲基類的一個字段,此字段以void *類型定義
b. 在派生類模塊中對基類此字段進行初始化
c. 對外不暴露派生類類型,但暴露基類類型
d. 多用於業務邏輯/流程在基類模塊中實現的場景,派生類爲基類提供個性化服務優化
struct super_block { /* omitted */ void *s_fs_info; /* Filesystem private info */ /* omitted */ }; int autofs_fill_super(struct super_block *s, void *data, int silent) { struct autofs_sb_info *sbi; /* omitted */ sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); if (!sbi) goto fail_unlock; s->s_fs_info = sbi; /* omitted */ }
要點:
a. 派生接口對象經過字段inherits繼承基接口對象。
b. 派生接口對象經過ata_finalize_port_ops初始化。
c. C99中,若數據結構體中的某個字段被重複初始化,最後一次初始化有效。根據這個特色,實現接口成員的覆蓋功能。ui
struct ata_port_operations { /* omitted */ void (*sff_dev_select)(struct ata_port *ap, unsigned int device); /* omitted */ /* * ->inherits must be the last field and all the preceding * fields must be pointers. */ const struct ata_port_operations *inherits; }; const struct ata_port_operations ata_base_port_ops = { .prereset = ata_std_prereset, .postreset = ata_std_postreset, .error_handler = ata_std_error_handler, }; const struct ata_port_operations sata_port_ops = { .inherits = &ata_base_port_ops, .qc_defer = ata_std_qc_defer, .hardreset = sata_std_hardreset, }; const struct ata_port_operations sata_pmp_port_ops = { .inherits = &sata_port_ops, .pmp_prereset = ata_std_prereset, .pmp_hardreset = sata_std_hardreset, .pmp_postreset = ata_std_postreset, .error_handler = sata_pmp_error_handler, }; static void ata_finalize_port_ops(struct ata_port_operations *ops) { static DEFINE_SPINLOCK(lock); const struct ata_port_operations *cur; void **begin = (void **)ops; void **end = (void **)&ops->inherits; void **pp; if (!ops || !ops->inherits) return; spin_lock(&lock); for (cur = ops->inherits; cur; cur = cur->inherits) { void **inherit = (void **)cur; for (pp = begin; pp < end; pp++, inherit++) if (!*pp) *pp = *inherit; } for (pp = begin; pp < end; pp++) if (IS_ERR(*pp)) *pp = NULL; ops->inherits = NULL; spin_unlock(&lock); }
要點:設計
a. 基類接口對象用宏實現。
b. 派生接口對象經過直接內嵌基類接口對象宏來實現繼承。
c. C99中,若數據結構體中的某個字段被重複初始化,最後一次初始化有效。根據這個特色,實現接口成員的覆蓋功能。
d. 只適用於兩層繼承結構。
#define BCM_XXX_OPS .dwBoardId = 0xffff, \ .trunk_id_translate = NULL, \ .trunk_add_port = bcm_XXX_trunk_add_port, \ ... \ .trunk_del_port = bcm_XXX_trunk_del_port BCM_OPERATIONS bcm_XXX_table[]= { {BCM_XXX_OPS}, /*default*/ {BCM_XXX_OPS, .dwBoardId = _BT_3G_USRUD, .pre_config = bcm_XXX_pre_config, .packet_pro_config = bcm_XXX_packet_pro_config}, {BCM_XXX_OPS, .dwBoardId = _BT_3G_HMPUE, .pre_config = bcm_XXX_pre_config, .packet_pro_config = bcm_XXX_packet_pro_config}, {_BT_3G_NULL,0}, };
要點:
a. 通用操做,如:構造函數、解析函數、比較函數、複製函數等
b. 函數模板參數以 void * 定義
c. 把相關模板函數轉化爲純虛基類的成員函數
d. 模板函數基於純虛基類上實現
e. 每一個派生類繼承純虛基類,純虛基類以關聯方式繼承,類型爲const void *以便於信息隱藏
f. 全部基類必須做爲派生類第一個成員,因此不支持多重繼承方式
g. 派生類具體實現本身特定接口
/* new.h */ void * new (const void * class, ...); void delete (void * item); void * clone (const void * self); int differ (const void * self, const void * b); size_t sizeOf (const void * self); /* new.r */ struct Class { size_t size; void * (* ctor) (void * self, va_list * app); void * (* dtor) (void * self); void * (* clone) (const void * self); int (* differ) (const void * self, const void * b); }; /* new.c */ void * new (const void * _class, ...) { const struct Class * class = _class; void * p = calloc(1, class -> size); assert(p); * (const struct Class **) p = class; if (class -> ctor) { va_list ap; va_start(ap, _class); p = class -> ctor(p, & ap); va_end(ap); } return p; } /* string.h */ extern const void * String; /* string.c */ struct String { const void * class; /* must be first */ char * text; }; static void * String_ctor (void * _self, va_list * app) { struct String * self = _self; const char * text = va_arg(* app, const char *); self -> text = malloc(strlen(text) + 1); assert(self -> text); strcpy(self -> text, text); return self; } static const struct Class _String = { sizeof(struct String), String_ctor, String_dtor, String_clone, String_differ }; const void * String = & _String; /* app.c */ int main () { void * a = new(String, "a"); //... delete(a); return 0; }
要點:
a. 函數定義成宏表達式,由於宏表達式不攜帶類型。固然這損失了代碼可維護性。
b. 函數參數類型經過C關鍵詞typeof獲取
c. 變量讀寫儘可能利用內存操做,由於內存操做可泛化具體數據類型,經過union和sizeof獲取參數首址和長度。
#define WRITE_ONCE(x, val) \ ({ \ union { typeof(x) __val; char __c[1]; } __u = \ { .__val = (__force typeof(x)) (val) }; \ __write_once_size(&(x), __u.__c, sizeof(x)); \ __u.__val; \ }) static __always_inline void __write_once_size(volatile void *p, void *res, int size) { switch (size) { case 1: *(volatile __u8 *)p = *(__u8 *)res; break; case 2: *(volatile __u16 *)p = *(__u16 *)res; break; case 4: *(volatile __u32 *)p = *(__u32 *)res; break; case 8: *(volatile __u64 *)p = *(__u64 *)res; break; default: barrier(); __builtin_memcpy((void *)p, (const void *)res, size); barrier(); } }
要點:
a. 構造調用服務對象,來封裝函數及其參數信息,不一樣的函數經過服務id來識別
b. 經過可變參數實現不一樣服務的參數提取
#include <stdio.h> #include <stdarg.h> struct call_svc { long id; long func; long arg_num; }; static int a(void) { printf("%s(void)\n", __FUNCTION__); return 0; } static int b(long arg1) { printf("%s(%ld)\n", __FUNCTION__, arg1); return 0; } static int c(long arg1, long arg2, char* str) { printf("%s(%ld, %ld, %s)\n", __FUNCTION__, arg1, arg2, str); return 0; } static struct call_svc svc[] = { {0, (long)&a, 0}, {1, (long)&b, 1}, {2, (long)&c, 3}, }; static int call(long id, ...) { int svc_num = sizeof(svc)/sizeof(svc[0]); int i ,ret; va_list argptr; for(i=0; i<svc_num; i++) { if(id == svc[i].id) break; } if(i >= svc_num) { printf("unsupported svc id: %ld\n", id); return -1; } va_start(argptr, svc[i].arg_num); switch (svc[i].arg_num) { case 0: ret = ((int (*)())svc[i].func)(); break; case 1: { long v1 = va_arg( argptr, long); ret = ((int (*)(long))svc[i].func)(v1); } break; case 3: { long v1 = va_arg( argptr, long); long v2 = va_arg( argptr, long); char *v3 = va_arg( argptr, char *); ret = ((int (*)(long, long, char *))svc[i].func)(v1, v2, v3); } break; default: printf("unsupported svc param number. id: %ld, arg_num: %ld.\n", id, svc[i].arg_num); break; } va_end(argptr); return ret; } int main(void) { call(0); call(1, 11); call(2, 11, 22, "hello world"); return 0; }