文章目录
- 1. 背景介绍
- 2. 设计实现
- 3. 运行测试
- 4. 总结
1. 背景介绍
印象中,设计模式是由面向对象的语言(C++、JAVA)才能完成的,而 C 语言是面向过程的语言,不能实现设计模式。但C 语言中有 函数指针、回调函数 等机制,使用这些机制便能写出面向对象的优秀程序。
LINUX 操作系统,采用 C 语言写的,但是里面很多模块实现都是通过面向对象的设计方式实现的,这也是很多人初看 Linux 源码看得云里雾里的原因。
面向过程(Procedure Oriented 简称 PO): 把事情拆分成几个步骤(相当于拆分成一个个的方法和数据),然后按照一定的顺序执行。
面向对象(Object Oriented 简称 OO): 面向对象会把事物抽象成对象的概念,先抽象出对象,然后给对象赋一些属性和方法,然后让每个对象去执行自己的方法。
2. 设计实现
在 C++中实现设计模式主要是通过virtual 关键字修饰的虚函数来实现的,在 C 语言中没有这个操作,但是可以通过指针函数加结构体进行实现。先简单了解一下指针函数:
typedef void (*pfunc)(int); //此处定义了pfunc这个函数指针
//定义一个test_func函数,与pfunc的返回类型和参数是一致的,只有名字不同
//若不一致则不能定义pfunc的指针指向test_func
void test_func(int id)
{
printf("id=%d \n", id);
}
//使用func指向test_func()
pfunc func = test_func;
//调用func(id) 与 test_func(id)实现的功能一致
func(1);//即可使用
//与结构体结合
typedef struct func_t
{
void (*func)(int id);
}func_t;
func_t f;
f.func = test_func; //结构体的函数指针指向test_func()
f.func(1); //调用结构体的函数指针,与调用test_func(1)效果一致
//通过上述例子,可以看到通过结构体+函数指针可以实现封装信息并指向另外一个函数
//有了这个特性可以实现一个简单工厂模式
本设计实现一个简单的工厂模式,一个生产不同种类水果的工厂。每种水果有两个属性show() eat()
,实现 apple、banana、pear
这 3 种水果。
//设计抽象定义接口
//定义的抽象的水果接口
typedef struct Ifruit_t
{
void (*show)(void* obj); //显示信息
void (*eat)(void* obj); //怎么eat
void *obj; //指向当前的结构体
}Ifruit_t;
下面程序是香蕉水果相关的定义
, 另外的苹果与梨
定义与这个几乎完全一致,此处考虑篇幅问题,不全部贴出来了。
//------------------------------------------------
//实现香蕉相关的定义
//------------------------------------------------
typedef struct banana_t //与Ifruit_t的定义一致
{
void (*show)(void* obj); //显示信息
void (*eat)(void* obj); //怎么eat
void *obj; //指向当前的结构体
}banana_t;
static void banana_show(void* obj) //使用static修饰,避免被外部直接调用
{
printf("我是香蕉!\n");
}
static void banana_eat(void* obj) //使用static修饰,避免被外部直接调用
{
printf("操作: 先剥掉皮,再吃!\n");
}
//香蕉的构造函数
banana_t* constructor_banana(void) //不使用static修饰,让外部直接调用
{
banana_t* obj = (banana_t*)malloc(sizeof(banana_t));
obj->show = banana_show; //给指针函数赋值,后面才能被调用
obj->eat = banana_eat; //给指针函数赋值,后面才能被调用
obj->obj = obj; //obj指向当前结构体指针
return obj;
}
工厂实现的函数
如下:
enum FruitType //枚举类型
{
APPLE, //苹果
BANANA, //香蕉
PEAR, //梨
};
Ifruit_t* factor_create_fruit(enum FruitType type) //工厂:生成水果的
{
Ifruit_t *fruit=NULL;
switch (type)
{
case APPLE:
fruit = (Ifruit_t *)constructor_apple();
printf("工厂: 生产苹果!\n");
break;
case BANANA:
fruit = (Ifruit_t *)constructor_banana();
printf("工厂: 生产香蕉!\n");
break;
case PEAR:
fruit = (Ifruit_t *)constructor_pear();
printf("工厂: 生产梨!\n");
break;
default:
break;
}
return fruit;
}
main 使用流程
如下。工厂设计模式它的优势是易于扩展,此处实现了香蕉、苹果、梨种水果,当业务中需要西瓜时,写一个西瓜相关的结构体并实现对应函数,实现方式和上面 banana 实现方式一致,而对于西瓜的使用依旧是如下调用fruit->show(NULL); fruit->eat(NULL);
,这样主体的业务逻辑便能完成以较小的改动来添加一个新的模块功能。
int main(void)
{
Ifruit_t *fruit=NULL;
fruit = factor_create_fruit(APPLE); //生成苹果
//每一次有新的水果添加进来,步骤都和下面一样的,易于扩展
fruit->show(NULL); //显示苹果
fruit->eat(NULL); //操作苹果
free(fruit); //不使用了,释放资源
printf("\n");
fruit = factor_create_fruit(BANANA);
fruit->show(NULL);
fruit->eat(NULL);
free(fruit);
printf("\n");
fruit = factor_create_fruit(PEAR);
fruit->show(NULL);
fruit->eat(NULL);
free(fruit);
return 0;
}
3. 运行测试
运行结果:
4. 总结
虽然C语言是面向过程的编程语言,但是在设计程序的时候,可以考虑用面向对象的方式去设计,这样提高程序的“高内聚、低耦合”特性,便于维护。