C語言面對對象設計模式彙編

面向對象發展到今天,已經出現了許許多多優秀的實踐、方法和技術。不少的技術都可以有效的提升軟件質量。IBM上的《面向對象軟件開發和過程》系列文章對面對對象設計從以下層面進行了詳細的介紹:代碼是核心、 案例實戰(上)、 案例實戰(下)、 重用、優化代碼的組織、 針對契約設計、 業務建模。html

雖然文章中的案例實戰或代碼示例都是以Java、C++等面對對象語言爲主,可是輔以必定的方法和工具,C語言同樣能作出優秀的面對對象設計,由於軟件設計的基本理念原則是相通的。C語言面對對象設計的主要難點在於,對於抽象、繼承、泛型(模板)等面對對象設計手段,在語法層次上缺乏直接的支持。linux

本文試圖對以下四個主要的面對對象設計手段提供C語言的解決方案和示例。數據結構

  • 抽象數據類型
  • 對象繼承(又叫實現繼承)
  • 接口繼承
  • 函數模板

抽象數據類型

抽象數據類型:方法A

要點:
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);

抽象數據類型:方法B

要點:
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

要點:
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);
}

  

對象繼承:方法B

要點:
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)

  

對象繼承:方法C

要點:
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;
}
相關文章
相關標籤/搜索