我中意的C面向对象编程模式

来源:互联网 发布:赚钱的交友软件 编辑:程序博客网 时间:2024/04/27 19:22

        不管是接入网还是传输网产品,控制平面代码,上层需要接收网管NMS管理配置API,以及协议相关管理配置;底层需要操作数据平面的API,而且上层可以会修改接口API,数据平面可能会更改芯片方案。

        这就涉及到代码模块化的设计,控制平面代码的模块化,需要做到模块独立(一个人维护一个或者N个模块,利于分工)即使芯片方案修改,也只需要更改API接口即可

        网管NMS是Java编码的,Java是面向对象设计语言,在界面上的操作的思想,可以完全理解到控制平面的C实现上,和Java的实现一一对象

        Java和C++的类,继承,构造函数,析构函数,虚函数,多态,Factory Method工厂方法这些面向对象的特性,C也可以很好的实现,下面结合在项目过程中的个人爱好,来说明用C实现面向对象编码

        例子采用person基类,student和teacher子类来说明

        animal是和person平行的基类,可能有N多个这样的基类


        模块可能有用很多的Module,Person是其中一个,module_person_***是顶层API下的多态接口,PERSON_OBJECT是Person模块的全局结构

(animal对应就是module_animal_***,ANIMAL_OBJECT就是Animal的全局结构,实际中的全局数据就放这里)

(PERSON_OBJECT/ANIMAL_OBJECT类似于log4c中的log4c_category_factory/log4c_appender_factory/log4c_layout_factory,person_object_t和animal_object_t类似于sd_factory_t,这里没有做factory的管理,没有采用factory工厂注册机制,是为了模块做适当的分离)

        object_factory_ops_t是object的工厂,fac_new,fac_delete,fac_print工厂的实现基本接口(factory_ops),module_**_**只和fac_**交互

(对应的Person Object就是person_factory_ops,person_new(person_fac_new),person_delete,person_print

Animal Object就是animal_factory_ops,animal_new,animal_delete,animal_print)

        Person是一个Object,Person基类有Student和Teacher两个子类

        person_class_t是基类,person_descriptor_t是对象描述符,包含ctor(constructor)构造函数,dtor(desconstructor)析构函数,以及desc_modify和desc_dosomthing等等虚函数,

1.      list.h

        list.h是linux提供的一个双向链表头文件,位于include/linux/list.h,在数据量比较大的时候,采用链表是个不错的方法,最常用的宏定义是

#define INIT_LIST_HEAD(ptr) do { \

    (ptr)->next = (ptr); (ptr)->prev = (ptr); \

} while (0)

#define list_entry(ptr, type, member) \

    ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))

#define list_for_each(pos, head) \

    for (pos = (head)->next; pos != (head); \

            pos = pos->next)

        list_entry是典型的多态应用,type就是struct结构体类型

 

typedef struct object_factory_ops_s

{

    /* dont use poly, only singleton */

    void *(*fac_new)();

    void (*fac_delete)();

    void (*fac_print)();

}object_factory_ops_t;

 

typedef struct person_object_s

{

    struct list_head person_list_head;

    object_factory_ops_t *factory_ops; 

}person_object_t;

person_object_t PERSON_OBJECT;

void person_init()

{

    INIT_LIST_HEAD(&(PERSON_OBJECT.person_list_head));

    PERSON_OBJECT.factory_ops = &person_factory_ops;       

}

        上面是对全局的person模块PERSON_OBJECT成员初始化,这里只是简单的挂接了函数指针

list_for_each(pos, &(PERSON_OBJECT.person_list_head))

{

    pthis = list_entry(pos, person_class_t, person_list);

    (PERSON_OBJECT.factory_ops->fac_print)(pthis);     

    person_cnt++;

}

        上面是object_**_**代码中的一部分,主要功能就是遍历PERSON_OBJECT.person_list_head这个链表,得到基类(实际得到的只是指针,看起来是基类,内存同时指向的是一个subclass),并进行打印

 

2.      类/封装/Factory Method工厂方法

        person_class_t是基类,第一个成员desc是对象描述符(必须作为首成员),在person中就存在两种子类,Student和Teacher,他们的构造函数,析构函数,虚函数都不同,这里利用person_type来区分,采用数组就可以管理(如果子类非常的多,也可以用desc_list链表管理,但是实际中,是不太可能的,毕竟子类是已知的,所以没有必要采用链表管理的);

        person_type用来区分子类;

        name和age是person的基本属性(teacher/student共有的);

        person_list是用来记录本结构体的链,每一个结构的链都会添加到PERSON_OBJECT.person_list_head中,用它来管理数据信息

typedef struct person_descriptor_s

{

    char *person_type;

    //struct list_head desc_list;

    int size;   /* size of subclass */

    void *(*desc_ctor)();

    void *(*desc_dtor)();

    int (*desc_modify)();

    int (*desc_dosomthing)();

}person_descriptor_t;

 

typedef struct person_class_s

{

    person_descriptor_t *desc; /* must be first */

    char  person_type[32];

    char name[32];

    int age;

    struct list_head person_list;

}person_class_t;

 

3.      继承

        在上面封装中的对象描述符中的size是子对象的结构体大小,也就是说内存中有N份person_class_t的记录,实际上大小根据person_type的不同而不同

        下面结构就是子类继承了基类person的结构,base必须做为结构的首成员

typedef struct person_stu_s

{

    person_class_t base; /* must be first */

    int student_id;

    int score;

}person_stu_t;

typedef struct person_teach_s

{

    person_class_t base; /* must be first */

    int salary;

    int bonus;

    char *post;

}person_teach_t;

        下面是子类的构造函数和析构函数以及虚函数组成的结构

const person_descriptor_t person_stu_desc =

{

    "student",

    //{0, 0},  

    sizeof(person_stu_t),

    person_stu_ctor,

    person_stu_dtor,

    person_stu_modify,

    person_stu_dosomthing

};

const person_descriptor_t person_teach_desc =

{

    "teacher",

    //{0, 0},  

    sizeof(person_teach_t),

    person_teach_ctor,

    person_teach_dtor,

    person_teach_modify,

    person_teach_dosomthing

};

 

4.      虚函数/多态

        多态,简单理解就是动态连接到通用函数

        对于Person其中的Student和Teacher来说,操作接口已经封装好了,现在要做的就是在Student的时候,动态连接到Student的构造/析构/虚函数上去

        下面是一个完整的创建Student学生信息的流程

module_person.c:

        对外提供的API

int module_person_new(char *person_type, void *data)

{

    person_class_t *pthis = NULL;

    pthis = (person_class_t *)(PERSON_OBJECT.factory_ops->fac_new)(person_type, data);

    printf("\nname:%s,age:%d\n", pthis->name, pthis->age);

   

    return OK;

}

 

Person.c:

        C++中每个声明了虚函数的对象都带有vptr(virtual table pointers),它是一个看不见的数据成员,指向对应类的virtual table,这个看不见的数据成员也称为vptr,被编译器加在对象里,位置只有才编译器知道

        这里用的技巧是创建一个指针数组,讲子类描述符集合在一起,根据person_type来判断动态连接到哪个子类(相当于C++中的vptr,但是又和C++的RTTI机制类似)

        _pthis->size这个size大小,根据动态得到的子类结构不同而大小不同

const person_descriptor_t* person_desc_types[] =

{

    &person_stu_desc,

    &person_teach_desc,

    NULL

}; 

 

void *person_new(char *person_type, void *data)

{

    person_descriptor_t *_pthis = NULL;

    int type_idx = 0;

   

    while (NULL != person_desc_types[type_idx])

    {

        if (!strcmp(person_type, person_desc_types[type_idx]->person_type))

        {

            _pthis = person_desc_types[type_idx];

            break;

        }    

        type_idx++;  

    }

 

    (void *)pthis = (void *)malloc(_pthis->size);

    * (const person_descriptor_t **)pthis = _pthis;

    if (_pthis->desc_ctor)

    {  

        pthis = _pthis->desc_ctor(pthis, data);

    }

   

    return pthis;

}

 

Person_student.c:

        person_class_ctor用来创建子类间的公用信息,子类独有的信息,自己构造函数创建

void *person_stu_ctor(void *self, cfg_person_stu_t *data)

{

    if ((NULL == self)||(NULL == data))

        return NULL;

 

    /* construct class data */

    self = person_class_ctor(self, data);

    if (NULL == self)

        return NULL;

 

    person_stu_t *pthis = (person_stu_t *)self;

    pthis->score = data->score;

    pthis->student_id = data->student_id;

    strcpy(((person_class_t *)pthis)->person_type, "student");

 

    return self;   

}

 

Person_class.c:

        Student和Teacher的公共数据信息,class就相当于C++中的虚基类,可以用下面模块来实现

        list_add_tail(&(self->person_list), &(PERSON_OBJECT.person_list_head));是将类(这里可以理解为子类)的N多对象加到person的链表中方便管理

void *person_class_ctor(person_class_t *self, cfg_person_t *data)

{

    if ((NULL == self)|(NULL == data))

        return NULL;

    strcpy(self->name, data->name);

    self->age = data->age;

    list_add_tail(&(self->person_list), &(PERSON_OBJECT.person_list_head));

   

    return self;

}

        上面是一个创建的多态基本流程,析构的过程刚好相反,虚函数同样机制

 

5.      小结

        采用C面向对象来编码,模块清晰明了,同事与同事之间的工作独立性强,代码维护起来更容易,person.c/person_student.c/person_teacher.c/person_class.c的函数都非常好的隐藏起来,只有fac_new,fac_delete,fac_print暴露给上层API来操作

        不同Module也可以采用类似来管理,这样就是网管NMS的顶层API---->多态Module---->factory_ops---->多态子类(基类Class)---->底层API

        这样的效果就是不管底层API,或者上层API修改了,控制平面代码改动量也非常的小

        上面采用C实现了基类,子类,继承,虚基类,构造函数,析构函数,虚函数,多态,Factory Method工厂方法,但是对比C++,比如friend,多重继承,等特性,C语言实现稍显麻烦,但是在嵌入式领域中这些C++的高级特性,至少在我参与的通信设备中,几乎用不到

原创粉丝点击