往期回顾:
3. 用C语言实现原型模式!
6. 用C语言实现适配器模式!
👀
1.概念
Template Method 模式也叫模板方法模式,是行为模式之一,它把具有特定步骤算法中的某些必要的处理委让给抽象方法,通过子类继承对抽象方法的不同实现改变整个算法的行为。
👀
2.应用场景
Template Method 模式一般应用在具有以下条件的应用中:
1. 具有统一的操作步骤或操作过程。
2. 具有不同的操作细节。
3. 存在多个具有同样操作步骤的应用场景,但某些具体的操作细节却各不相同。
👀
3.实例设计
1.本篇以 CPU 初始化流程为例对模板模式进行说明,例如 CPU 的初始化流程都大致一样,初始化硬盘、外设、内存、网络等,但是每个 CPU 初始化具体的细节却不相同,即初始化流程可以看作一个模板,有统一的操作步骤,但具有不同的操作细节。下面以 Intel CPU 和 AMD CPU 初始化为例。
2.首先定义抽象接口:初始化外设、硬盘、内存、网络等,init()函数即是模板,包含整个初始化流程,即外部调用一个 init()函数即可对 CPU 进行初始化。
typedef struct Interface_t
{
/*初始化外设USB、SPI、IIC等*/
void (*init_peripheral)(void *obj);
/*初始化硬盘*/
void (*init_disk)(void* obj);
/*初始化内存*/
void (*init_memory)(void* obj);
/*初始化网络*/
void (*init_net)(void *obj);
/*对整个流程初始化*/
void (*init)(void *obj);
}Interface_t;
3.AMD CPU 初始化相关定义,Intel CPU 相关代码实现与下面的实现几乎一致,此处不再贴代码。
//定义一个Intel CPU初始化结构体
typedef struct AMDCpuStart_t
{
void (*init_peripheral)(void *obj); //初始化外设
void (*init_disk)(void* obj); //初始化硬盘
void (*init_memory)(void* obj); //初始化内存
void (*init_net)(void *obj); //初始化网络
void (*init)(void *obj); //模块 整个初始化流程
}AMDCpuStart_t;
static void ADM_init_peripheral(void *obj)
{
printf("初始化AMD 外设...\n");
}
static void ADM_init_disk(void *obj)
{
printf("初始化AMD 硬盘...\n");
}
static void ADM_init_memory(void *obj)
{
printf("初始化AMD 内存...\n");
}
static void ADM_init_net(void *obj)
{
printf("初始化AMD 网络...\n");
}
//初始化模板
static void ADM_init(void *obj)
{
AMDCpuStart_t *amd = (AMDCpuStart_t*)obj;
printf("AMD CPU 上电初始化流程: \n");
amd->init_peripheral(amd);
amd->init_disk(amd);
amd->init_memory(amd);
amd->init_net(amd);
}
//AMD构造函数 创建一个结构体指针
AMDCpuStart_t* construct_amd_cpu(void)
{
AMDCpuStart_t* obj = (AMDCpuStart_t*)malloc(sizeof(AMDCpuStart_t));
obj->init = ADM_init;
obj->init_disk = ADM_init_disk;
obj->init_memory = ADM_init_memory;
obj->init_net = ADM_init_net;
obj->init_peripheral = ADM_init_peripheral;
return obj;
}
👀
4.测试与验证
测试程序:如下,先创建一个 AMD 的对象,调用 init()模块进行初始化,然后对 init_peripheral()、init_disk()指向新的函数,修改接口,这样保证了我们模块流程不变,但是可以改变一些具体的细节。最后创建 Intel 对象,调用 init()函数模板进行初始化。
//二次重写 AMD 外设初始化程序
void ADM_init_peripheral_v1(void *obj)
{
printf("新接口: 初始化 ADM 的外设: USB3.0、SPI、IIC接口...\n");
}
//二次重写 AMD 硬盘初始化程序
void ADM_init_disk_V1(void *obj)
{
printf("新接口: 初始化AMD 硬盘: 三星固态硬盘(512G)、东芝固态硬盘(128G)...\n");
}
int main(void)
{
Interface_t *cpu=NULL;
//创建一个AMD对象
cpu = (Interface_t*)construct_amd_cpu();
//调用模板 初始化AMD CPU
cpu->init(cpu);
printf("\n二次重写的硬盘和外设初始化接口:\n");
//给函数指针二次赋值(等于重写),便于修改添加新概念
cpu->init_peripheral = ADM_init_peripheral_v1;
//给函数指针二次赋值(等于重写),便于修改添加新概念
cpu->init_disk = ADM_init_disk_V1;
//调用模块,修改了初始化硬盘、初始化外设接口
cpu->init(cpu);
free(cpu);
cpu = NULL;
printf("\n");
//创建一个Intel CPU对象
cpu = (Interface_t*)construct_intel_cpu();
//调用Intel CPU初始化模板
cpu->init(cpu);
free(cpu);
cpu = NULL;
return 0;
}
测试结果: 与预期一致,第二次调用amd的init()模板时,使用的就是新接口的功能。
5.总结
-
模板模式 总结: 在抽象类中统一操作步骤,并规定好接口;让子类实现接口。这样可以把各个具体的子类和操作步骤解耦合
-
虽然C语言是面向过程的编程语言,但是我们在设计程序的时候,可以考虑用面向对象的方式去设计,这样提高我们程序的“高内聚、低耦合”特性,便于维护。
需要C语言实现设计模式代码的小伙伴:在微信公众号【Linux编程用C】后台回复 【designer】 即可获取,不断更新中!
欢迎大家加小C微信【LinuxCodeUseC】,我们一起交流讨论学习!
PS:若大家想看C语言版本的其他设计模式,
请大家 点赞! 转发!关注吧!~~