C - 语言设计模式¶
设计模式如下分为创建型模式,结构型模式,行为型模式。
创建型模式¶
1、抽象工厂模式(Abstract Factory)¶
介绍: 抽象工厂模式是一种创建型设计模式;
作用: 提供统一的接口让用户根据不同的需求创建出不同的工厂;
类比: 一个制造家具的工厂,可以根据用户不同的诉求,制造出不同风格的家具;
#include "common.h"
#include <assert.h>
/***************************************************************
* @brief: 一个制造家具的工厂,可以根据用户不同的诉求,制造出不同风格的家具;
**************************************************************/
void modern_chair(void)
{
printf("A modern chair\r\n");
}
void modern_desk(void)
{
printf("A modern desk\r\n");
}
void victorian_chair(void)
{
printf("A victorian chair\r\n");
}
void victorian_desk(void)
{
printf("A victorian desk\r\n");
}
typedef struct
{
void (*chair)(void);
} chair_t;
typedef struct
{
void (*desk)(void);
} desk_t;
chair_t *make_modern_chair(void)
{
chair_t *pChair = (chair_t *)malloc(sizeof(chair_t));
assert(pChair != NULL);
pChair->chair = modern_chair;
return pChair;
}
chair_t *make_victorian_chair(void)
{
chair_t *pChair = (chair_t *)malloc(sizeof(chair_t));
assert(pChair != NULL);
pChair->chair = victorian_chair;
return pChair;
}
desk_t *make_modern_desk(void)
{
desk_t* pDesk = (desk_t *)malloc(sizeof(desk_t));
assert(pDesk != NULL);
pDesk->desk = modern_desk;
return pDesk;
}
desk_t *make_victorian_desk(void)
{
desk_t *pDesk = (desk_t *)malloc(sizeof(desk_t));
assert(pDesk != NULL);
pDesk->desk = victorian_desk;
return pDesk;
}
typedef struct
{
chair_t *(*make_chair)(void);
desk_t *(*make_desk)(void);
} furnitureFactory;
#define MODERN "modern"
#define VICTORIAN "vitorian"
furnitureFactory *Creat_furnitureFactory(char* type)
{
furnitureFactory *pfurnitureFactory = (furnitureFactory *)malloc(sizeof(furnitureFactory));
assert(pfurnitureFactory != NULL);
if (memcmp(type, MODERN, sizeof(MODERN)) == 0) {
printf("modern mode!!!!\n");
pfurnitureFactory->make_chair = make_modern_chair;
pfurnitureFactory->make_desk = make_modern_desk;
} else if (memcmp(type, VICTORIAN, sizeof(VICTORIAN)) == 0) {
printf("vitorian mode!!!!\n");
pfurnitureFactory->make_chair = make_victorian_chair;
pfurnitureFactory->make_desk = make_victorian_desk;
} else {
printf("type is invalid\r\n");
}
return pfurnitureFactory;
}
int main(void)
{
char type[] = "modern";
furnitureFactory *my_factory = Creat_furnitureFactory(type);
my_factory->make_chair()->chair();
my_factory->make_desk()->desk();
free(my_factory);
}
2、建造者模式(Builder)¶
建造者模式将复杂产品的构建过程封装分解在不同的方法中,使得创建过程非常清晰。它隔离了复杂产品 对象的创建和使用,使得相同的创建过程能够创建不同的产品。若几个产品之间存在较大的差异,则不适用建造者模式。面向对象里的建造者模式,对于C语言,就无需这么复杂了。
#include "common.h"
/***************************************************************
* @brief:模拟组装电脑
**************************************************************/
typedef struct computer{
void (*assemble_cpu)(void);
void (*assemble_memory)(void);
void (*assemble_harddisk)(void);
void (*build_computer)(void *);
} computer_t;
void assemble_intel_cpu(void)
{
printf("intel cpu!\n");
}
void assemble_samsung_memory(void)
{
printf("samsung memory!\n");
}
void assemble_hitachi_harddisk(void)
{
printf("hitachi harddisk!\n");
}
void build_body(void *computer)
{
if (computer == NULL) {
return;
}
computer_t *p = (computer_t *)computer;
p->assemble_cpu();
p->assemble_harddisk();
p->assemble_memory();
}
int main(void)
{
computer_t computer = { 0 };
computer.build_computer = build_body;
computer.assemble_cpu = assemble_intel_cpu;
computer.assemble_harddisk = assemble_hitachi_harddisk;
computer.assemble_memory = assemble_samsung_memory;
computer.build_computer(&computer);
}
3、工厂方法模式(Factory Method)¶
介绍: 工厂模式是一种创建型设计模式;
作用: 隐蔽具体的生产逻辑,根据不同的要求生产出不同的产品;
类比: 一个衣服工厂可以根据不同的诉求生产出不同面料的衣服;
#include "common.h"
#include <assert.h>
/***************************************************************
* @brief:模拟一个衣服工厂可以根据不同的诉求生产出不同面料的衣服
**************************************************************/
typedef enum {
COTTON,
LEATHER,
} clothingType;
typedef struct {
clothingType fabric;
void (*generateClothing)(void);
} clothing;
void make_cotton_clothes(void)
{
printf("Make cotton clothes\r\n");
}
void make_leather_clothes(void)
{
printf("Make leather clothes\r\n");
}
clothing *manufacture_clothing(clothingType fabric)
{
clothing *cl = (clothing *)malloc(sizeof(clothing));
assert(cl);
cl->fabric = fabric;
switch(fabric) {
case COTTON:
cl->generateClothing = make_cotton_clothes;
break;
case LEATHER:
cl->generateClothing = make_leather_clothes;
break;
default:
break;
}
return cl;
}
int main(void)
{
clothing *cl = manufacture_clothing(COTTON);
cl->generateClothing();
}
4、原型模式(Prototype)¶
介绍:原型模式是一种创建型设计模式,使你可以通过复制原型来创建一个一模一样的新的对象,无需考虑创建过程,数据类型等内容;
作用:创建一个与原型一模一样的对象; 解决问题:即使原型的状态有所改变,也可以创建出与其当前状态一致的对象省去了创建对象的复杂操作,不必关注创建对象的具体过程 类比:老师打印试卷,拿着原版试卷,可以复制出任意数量的试卷,原版试卷既为原型;
#include "common.h"
#include "list.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define VAR(name, id) name##id
/***************************************************************
* @brief:模拟试卷拷贝
**************************************************************/
typedef struct Paper
{
char *teacher_name;
int class;
char *question;
struct Paper *(*copy)(struct Paper* pPaper);
} paper_t;
/*原型的复制接口*/
paper_t* paper_copy(paper_t* pPaper)
{
paper_t *copy = (paper_t*)malloc(sizeof(paper_t));
access(copy);
memcpy(copy, pPaper, sizeof(paper_t));
return copy;
}
paper_t *clone(paper_t *pPaper)
{
return pPaper->copy(pPaper);
}
int main(void)
{
/*定义原型*/
paper_t origin_paper = {"Li", 5, "1 + 1 = ?", paper_copy};
/*复制10份试卷*/
for (int i = 0; i < 10; i++) {
paper_t *VAR(paper, i) = clone(&origin_paper);
printf("paper_%d => name: %s, class: %d, question: %s\r\n",
i, VAR(paper, i)->teacher_name, VAR(paper, i)->class, VAR(paper, i)->question);
}
return 0;
}
5、单例模式(Singleton)¶
介绍:单例模式是一种创建型设计模式,又称为单件模式,是最简单的设计模式之一;
保证一个类永远都只有一个实例;如果你已经创建了一个实例, 过一会儿还想再创建一个新的实例, 此时获得到的实例是之前已创建的实例, 而不是一个新的实例; 作用:提供一个访问该实例的全局接口;
解决问题:控制实例数量;避免全局使用的实例频繁创建与销毁;
类比: 地球只有一个,无论在什么场景下,我们口中所说的地球都是同一个地球;
#include "common.h"
/***************************************************************
* @brief: 提供一个访问该实例的全局接口;
**************************************************************/
/*共享资源大小*/
#define SHARED_MEM_SIZE 100
/*访问该实例的全局接口*/
void* memory_get(void)
{
/*注意static*/
static void* pMem = NULL;
/*当实例已经存在时直接返回这个实例*/
if(pMem) {
return pMem;
}
/*当实例不存在时创建一个新的实例*/
pMem = (void*)malloc(SHARED_MEM_SIZE);
assert(pMem);
return (void*)pMem;
}
https://blog.csdn.net/liuguang841118/article/details/124898593
结构型模式¶
1、代理模式(Proxy)¶
为其他对象提供一种代理以控制对这个对象的访问。
实际例子:现在有pc1、pc2、pc3,但是只有pc1有上网权限,但是pc2、pc3也想上网,此时应该怎么办呢?此时,我们需要做的就是在pc1上开启代理软件,同时把pc2、pc3的IE代理指向pc1即可。这个时候,如果pc2或者pc3想上网,那么报文会先指向pc1,然后pc1把Internet传回的报文再发给pc2或者pc3。这样一个代理的过程就完成了整个的上网过程。
#include "common.h"
#include <assert.h>
/***************************************************************
* @brief:模拟代理情形
* @param:
* @note:
* @Sample usage:
**************************************************************/
typedef struct
{
void (*ftp_request)(void);
void (*http_request)(void);
void (*smtp_request)(void);
} pc_client_t;
typedef struct
{
pc_client_t* pClient;
void (*process)(void *)
} proxy_t;
void ftp_request(void)
{
printf("request from ftp!\n");
}
void http_request(void)
{
printf("request from http!\n");
}
void smtp_request(void)
{
printf("request from smtp!\n");
}
void process(void *proxy)
{
assert(NULL != proxy);
proxy_t *data = (proxy_t *)proxy;
data->pClient->ftp_request();
data->pClient->http_request();
data->pClient->smtp_request();
}
int main(void)
{
pc_client_t pc_client = { 0 };
proxy_t proxy = { 0 };
pc_client.ftp_request = ftp_request;
pc_client.http_request = http_request;
pc_client.smtp_request = smtp_request;
proxy.process = process;
proxy.pClient = &pc_client;
proxy.process(&proxy);
return 0;
}
2、桥接模式(Bridge)¶
将抽象部分与它的实现部分分离,使它们都可以独立地变化。考虑装操作系统,有多种配置的计算机,同样也有多款操作系统。将操作系统和计算机分别抽象出来,让它们各自发展,减少它们的耦合度。当然了,两者之间有标准的接口。这样设计,不论是对于计算机,还是操作系统都是非常有利的。
#include "common.h"
/***************************************************************
* @brief:模拟cpu组装
**************************************************************/
typedef struct _cpu_t{
void (*assemble_cpu_imc)(struct _cpu_t *);
int type;
} cpu_t;
static void assemble_cpu_imp(cpu_t *cpu)
{
printf("%d cpu\n", cpu->type);
}
typedef struct {
void (*assemble_cpu)(cpu_t *cpu);
} computer_t;
/* 组装cpu*/
static void assemble_cpu(cpu_t *cpu)
{
cpu->assemble_cpu_imc(cpu);
}
#define LOW_TYPE 0X01
#define HIGH_TYPE 0X02
int main(void)
{
cpu_t myCPu = { 0 };
myCPu.type = LOW_TYPE;
myCPu.assemble_cpu_imc = assemble_cpu_imp;
computer_t myComp = { 0 };
myComp.assemble_cpu = assemble_cpu;
myComp.assemble_cpu(&myCPu);
}
3、组合模式(Composite)¶
又叫做“整体-部分模式”,它使树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以像处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。比较复杂。
https://blog.csdn.net/lbaihao/article/details/117820500
4、装饰者模式(Decorator)¶
使用装饰器模式 在C语言中可以实现类似多态的效果 可以很方便的进行拓展 上面这个函数 又被称为架构函数 下面是其它被装饰的函数
#include "common.h"
/***************************************************************
* @brief:模拟多态
**************************************************************/
typedef int (*fun)(int, int);
void wrapper(fun func, int num1, int num2)
{
printf("enter wrapper\n");
func(num1, num2);
printf("get out og wrapper\n");
}
int add(int num1, int num2)
{
int sum = num1 + num2;
printf("%d+%d=%d\n", num1, num2, sum);
return sum;
}
int sub(int num1, int num2)
{
int sub = num1 - num2;
printf("%d-%d=%d\n", num1, num2, sub);
return sub;
}
int mul(int num1, int num2)
{
int mul = num1 * num2;
printf("%d*%d=%d\n", num1, num2, mul);
return mul;
}
int main(int argc, char *argv[])
{
wrapper(add, 20, 30);
wrapper(sub, 20, 30);
wrapper(mul, 20, 30);
return 0;
}
5、外观模式(Facade)¶
外观模式也叫门面模式。外观模式就是提供一个统一的接口,用来访问子系统中的一群接口。外观模式定义了一个高层接口,让子系统更容易使用。下面的create就是统一的接口。
#include "common.h"
/***************************************************************
* @brief: 提供一个统一的接口,用来访问子系统中的一群接口
**************************************************************/
typedef struct {
void (*work)(void);
} computer_t;
void work(void)
{
printf("working\n");
}
typedef struct {
void (*contact)(void);
} phone_t;
void contact(void)
{
printf("contacting\n");
}
typedef struct _factory{
computer_t *computer;
phone_t *phone;
void (*create)(struct _factory *);
} factory_t;
void create(factory_t *fac)
{
printf("create\n");
if (fac == NULL) {
return;
}
fac->computer->work();
fac->phone->contact();
}
int main(void)
{
computer_t computer = {
.work = work,
};
phone_t phone = {
.contact = contact,
};
factory_t factory = {
.create = create,
.computer = &computer,
.phone = &phone,
};
factory.create(&factory);
}
6、适配器模式(Adapter)¶
工作原理
- 将一个模块的接口转换成另一种接口,转换后的接口可以兼容。
- 调用者(用户)不需要知道各模块内部的实现,方便增减模块,可以解耦,方便移植
在C语言中大概思路如下:
#include "common.h"
/***************************************************************
* @brief: 适配接口的实现——通过宏定义
**************************************************************/
#define RTOS_TYPE
static void create_freertos_1( void )
{
printf( "create_freertos_1\n" );
}
static void create_freertos_2( void )
{
printf( "create_freertos_2\n" );
}
static void create_ucos( void )
{
printf( "create_ucos\n" );
}
static void run_freertos_1( void )
{
printf( "run_freertos_1\n" );
}
static void run_freertos_2( void )
{
printf( "run_freertos_2\n" );
}
static void run_ucos( void )
{
printf( "run_ucos\n" );
}
typedef struct _OsAdapter {
void( *create )( void );
void( *run )( void );
} OsAdapter;
void create( void )
{
#ifdef RTOS_TYPE
create_freertos_1();
create_freertos_2();
#elif UCOS_TYPE
create_ucos();
#endif
}
void run( void )
{
#ifdef RTOS_TYPE
run_freertos_1();
run_freertos_2();
#elif UCOS_TYPE
run_ucos();
#endif
}
int main( void )
{
OsAdapter os = { NULL };
os.create = create;
os.run = run;
os.create();
os.run();
}
个人理解只是接口调用而已,如果用函数指针的方法,即改变函数指针的指向。
7、享元模式(Flyweight))¶
当并发做一件任务时,存在大量相似对象。这个时候如果该对象已经申请,就不必再去申请一个新的。针对大量相似对象场景,可以避免大量开销。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct _Template
{
int title;
int font;
int lineDistance;
void (*operate)(struct _Template *pTemplate);
} Template;
void operate(struct _Template *pTemplate)
{
if (pTemplate == NULL) {
return;
}
printf("title:%d, font:%d, lineDistance:%d\n", pTemplate->title, pTemplate->font, pTemplate->lineDistance);
}
typedef struct _TemplateFactory
{
Template **pTemplates;
int num;
int size;
Template* (*GetTemplate)(struct _TemplateFactory *pTemplateFactory, int title, int font, int lineDistance);
void (*FreeTemplates)(struct _TemplateFactory *pTemplateFactory);
} TemplateFactory;
Template *GetTemplate(struct _TemplateFactory *pTemplateFactory, int title, int font, int lineDistance)
{
int index = 0;
Template *pTemplate = NULL;
Template **pTemplates = NULL;
if (NULL == pTemplateFactory) {
return NULL;
}
for (;index < pTemplateFactory->num; ++index) {
if (title != pTemplateFactory->pTemplates[index]->title) {
continue;
}
if (font != pTemplateFactory->pTemplates[index]->font) {
continue;
}
if (lineDistance != pTemplateFactory->pTemplates[index]->lineDistance) {
continue;
}
return pTemplateFactory->pTemplates[index];
}
pTemplate = (Template *)malloc(sizeof(Template));
if (NULL == pTemplate)
{
return NULL;
}
pTemplate->title = title;
pTemplate->font = font;
pTemplate->lineDistance = lineDistance;
if (pTemplateFactory->num < pTemplateFactory->size)
{
pTemplateFactory->pTemplates[index] = pTemplate;
++(pTemplateFactory->num);
return pTemplate;
}
if (0 == pTemplateFactory->size) {
pTemplates = (Template **)malloc(sizeof(Template *));
} else {
pTemplates = (Template **)malloc(sizeof(Template *) * (2 * pTemplateFactory->size));
}
if (NULL == pTemplates) {
return NULL;
}
if (pTemplateFactory->size > 0) {
memmove(pTemplates, pTemplateFactory->pTemplates, pTemplateFactory->size);
free(pTemplateFactory->pTemplates);
}
pTemplateFactory->pTemplates = pTemplates;
pTemplateFactory->pTemplates[pTemplateFactory->size] = pTemplate;
if (pTemplateFactory->size > 0) {
pTemplateFactory->size *= 2;
} else {
pTemplateFactory->size = 1;
}
++(pTemplateFactory->num);
return pTemplate;
}
void FreeTemplates(struct _TemplateFactory *pTemplateFactory)
{
int index = 0;
Template *pTemplate = NULL;
for (;index < pTemplateFactory->size;index++) {
pTemplate = pTemplateFactory->pTemplates[index];
if (pTemplate) {
free(pTemplate);
}
}
if (pTemplateFactory->pTemplates) {
free(pTemplateFactory->pTemplates);
}
}
int main(void)
{
TemplateFactory templateFactory = { 0 };
templateFactory.GetTemplate = GetTemplate;
templateFactory.FreeTemplates = FreeTemplates;
Template *pTemplate = templateFactory.GetTemplate(&templateFactory, 1, 2, 3);
if (pTemplate != NULL) {
pTemplate->operate = operate;
pTemplate->operate(pTemplate);
}
templateFactory.FreeTemplates(&templateFactory);
return 0;
}
面向接口编程:
鸭子的行为特征——会飞和嘎嘎叫
行为型模式¶
1、职责链模式(Chain of Responsibility)¶
介绍:责任链模式是一种行为模式,它可以允许你将请求沿着处理者链进行发送,收到请求以后, 每个处理者均可对请求进行处理, 或将其传递给链上的下个处理者;
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include "list.h"
/***************************************************************
* @brief:模拟流程审阅,内核链表实现
**************************************************************/
enum {PASS, REFUSE, };
typedef struct {
int money;
char* files;
} info_t;
typedef int (*request_handle)(info_t* info);
typedef struct {
request_handle func;
struct list_head myList;
} chainNode;
chainNode *init_list(void)
{
chainNode *head = (chainNode *)malloc(sizeof(chainNode));
if (head == NULL) {
perror("malloc failed in init_list\n");
return NULL;
}
INIT_LIST_HEAD(&(head->myList));
return head;
}
chainNode *create_node(request_handle func)
{
chainNode *newNode = (chainNode *)malloc(sizeof(chainNode));
if (newNode == NULL) {
perror("malloc failed in create_node\n");
return NULL;
}
newNode->func = func;
INIT_LIST_HEAD(&(newNode->myList));
return newNode;
}
int leader_handle(info_t* info)
{
assert(info);
if(info->money < 1000) {
//金额小于1000就行
return PASS;
}
return REFUSE;
}
int hr_handle(info_t* info)
{
assert(info);
if(info->money < 2000 && info->files != NULL) {
//金额小于2000且材料齐全
return PASS;
}
return REFUSE;
}
int boss_handle(info_t* info)
{
assert(info);
if(info->files != NULL) {
//材料齐全就行,不差钱
return PASS;
}
return REFUSE;
}
#define PROCESS_NUM 3
//For this we only need to focus on the ordering of the elements of this array
request_handle req_table[] = {
hr_handle,
leader_handle,
boss_handle
};
int main(void)
{
chainNode *head = init_list();
/*将审批节点添加到责任链上*/
for (int i = 0; i < PROCESS_NUM; i++) {
chainNode *new_node = create_node(req_table[i]);
list_add_tail(&(new_node->myList), &(head->myList));
}
/*报销内容*/
info_t info = {.money = 900, .files = "files"};
chainNode *p = NULL;
chainNode *n = NULL;
list_for_each_entry_safe(p, n, &(head->myList), myList) {
if (p->func(&info) != PASS) {
printf("failed to to reimburse\n");
return EXIT_FAILURE;
}
}
printf("success to to reimburse, thank you\n");
return EXIT_SUCCESS;
}
2、命令模式(Command)¶
介绍: 命令模是一种行为模式,它可以使代码解耦,便于维护;
#include <stdio.h>
/***************************************************************
* @brief:命令模式实现事件处理
**************************************************************/
void eatting(void)
{
printf("eatting\n");
}
void running(void)
{
printf("running\n");
}
void sleeping(void)
{
printf("sleeping\n");
}
# 传统模式
void parsing_cmd_trandition_mode(int cmd)
{
switch (cmd)
{
case 0x01:
eatting();
break;
case 0x02:
running();
break;
case 0x03:
sleeping();
break;
default:
break;
}
}
typedef void (*parsing_cmd)(void);
typedef struct
{
int cmd;
parsing_cmd parsing_fun;
} cmd_t;
#define ACTION_NUM 3
static cmd_t cmd_table[ACTION_NUM] = {
{0x01, eatting},
{0x02, running},
{0x03, sleeping}
};
# 命令模式
void parsing_cmd_command_mode(int cmd)
{
for (int i = 0; i < ACTION_NUM; ++i) {
if (cmd == cmd_table[i].cmd) {
cmd_table[i].parsing_fun();
}
}
}
int main(int argc, char const *argv[])
{
parsing_cmd_trandition_mode(0x02);
parsing_cmd_command_mode(0x01);
parsing_cmd_command_mode(0x03);
return 0;
}
命令模式。在命令模式里面,我们只需要维护一个命令列表就行了,而不需要关注解析函数本身。 先来看一个通讯协议相关的例子,数据帧定义如下:
帧长度 | 传感器类型 | 数据 |
---|---|---|
1字节 | 1字节 | n字节 |
传感器数据格式定义如下
传感器 | 类型号 | 数据格式 |
---|---|---|
温度传感器 | 1 | 整型数据(4字节) |
湿度传感器 | 2 | 整型数据(4字节) |
#include "common.h"
// 当心字节对齐的问题
typedef struct {
u_int8_t head;
u_int8_t cmd;
u_int16_t length;
u_int8_t data[1];
} package_t;
static void parse_temperature(u_int8_t *buffer)
{
int value = *buffer;
printf( "temperature = %d\n", value);
}
static void parse_humidity(u_int8_t *buffer)
{
int value = *buffer;
printf( "humidity = %d\n", value);
}
static void parse_illumination(u_int8_t *buffer)
{
int value = *buffer;
printf( "illumination = %d\n", value);
}
typedef struct {
u_int8_t cmd;
void (* handle)(u_int8_t *);
} parse_handler;
static const parse_handler handlers[] = {
{0x01, parse_temperature},
{0x02, parse_humidity},
{0x03, parse_illumination},
{0xFF, NULL},
};
static u_int8_t parse(u_int8_t *buffer, u_int8_t length)
{
package_t *frame = (package_t *)buffer;
u_int8_t tail = buffer[length - 1];
const parse_handler *entry;
if (frame->head != 0xFF) {
return 0;
}
for (entry = handlers; entry->handle != NULL; ++entry) {
if (frame->cmd == entry->cmd) {
entry->handle(frame->data);
break;
}
}
return 1;
}
int main(void)
{
u_int8_t buffer[] = {0xFF, 0x01, 0x00, 0x05, 0x02};
parse(buffer, 5);
u_int8_t buffer_2[] = {0xFF, 0x02, 0x00, 0x05, 0x02};
parse(buffer_2, 5);
u_int8_t buffer_3[] = {0xFF, 0x03, 0x00, 0x05, 0x02};
parse(buffer_3, 5);
}
3、解释器模式(Interpreter)¶
设计函数,对指定输入进行解释,将输入解释为期望的输出结果。
#include "common.h"
/***************************************************************
* @brief:
**************************************************************/
typedef struct interpreter
{
int type;
void *pData;
int result;
int* (*process)(struct interpreter *);
} interpreter_t;
int *process_1(struct interpreter *interpreter)
{
...
}
int *process_2(struct interpreter *interpreter)
{
...
}
int main(void)
{
interpreter_t interpreter = { 0 };
interpreter.type = 0x01;
interpreter.pData = "helloworld";
interpreter.result = true;
interpreter.process = process_1;
if (interpreter.process(&interpreter)) {
...
}
}
4、迭代器模式(Iterator)¶
遍历一组数据结构或对象事物,不需要知道内部具体实现的方法。
5、中介者模式(Mediator)¶
当很多独立的个体之前存在大量交流时、所有消息转发给中介者,会大大降低代码交互复杂度
#include "common.h"
struct Customer {
struct Waiter* waiter;
int ( *request) (struct Customer* customer, int fish);
};
struct Cooker {
struct Waiter* waiter;
void ( *cook)(struct Cooker* cooker, int fish);
};
// 中介者waiter负责提供数据供双方访问,类似于全局变量
struct Waiter {
int fish;
// 当customer指向waiter后,就可以通过访问waiter中的结构体来访问其他对象
struct Customer* customer;
struct Cooker* cooker;
int ( *wrequest)(struct Customer* customer, int fish); //customer
};
void cook(struct Cooker* cooker, int fish)
{
cooker->waiter->fish = fish;
}
int request(struct Customer* customer, int fish)
{
// 调用中介者中的函数 ,使维护集中在中介者中
return customer->waiter->wrequest(customer, fish);
}
int wrequest(struct Customer* customer, int fish)
{
// 很多资源和函数都集中在中介者中,
if (fish > customer->waiter->fish) {
printf ("抱歉,厨师反馈没有 %d 那么多鱼了,重新点菜吧。\n", fish);
return -1;
} else {
printf ("点菜成功,厨房还有%d鱼,足够你们吃了\n", fish);
return 0;
}
}
int fish(void)
{
return (rand() % 50 + 40);
}
int main(void)
{
struct Customer customer = { 0 };
struct Cooker cooker = { 0 };
struct Waiter waiter = { 0 };
waiter.wrequest = wrequest;
cooker.waiter = &waiter;
customer.waiter = &waiter;
cooker.cook = cook;
customer.request = request;
cooker.cook (&cooker, 50); //厨师今天只能做50条鱼
while (customer.request (&customer, fish())); //消费者跟服务员waiter说要个N条大鱼
}
6、访问者模式(Visitor)¶
访问者模式(Visitor)介绍 把对象数据和操作分离,使操作可以独立演化。一旦这些操作需要修改的话,接受这个操作的数据结构可以保持不变。访问者模式是适用于那些数据结构比较稳定的模式。访问者模式就是不同的人对不同的事物有不同的感觉。
#include "common.h"
#define OK 0
#define U_OK 1
/***************************************************************
* @brief:模拟不同的人对不同豆腐类型的喜好
**************************************************************/
typedef enum {
SPICY_FOOD,
STRONG_SMELL_FOOD,
} toufuType_e;
typedef enum {
WEST,
EAST,
} region_e;
typedef struct _tofu {
int type;
int (*eat)(void *, void *);
} tofu_t;
typedef struct _vistor {
int region;
int (*process)(void *, void *);
} vistor_t;
int process(void *tofu, void *vistor)
{
if (tofu == NULL || vistor == NULL) {
return U_OK;
}
tofu_t *p_tofu = (tofu_t *)tofu;
vistor_t *p_vistor = (vistor_t *)vistor;
if ((p_tofu->type == SPICY_FOOD && p_vistor->region == WEST ) ||
(p_tofu->type == STRONG_SMELL_FOOD) && p_vistor->region == EAST) {
printf("it tastes so good\n");
return OK;
}
printf("I don't like it\n");
return U_OK;
}
int eat(void * vistor, void * tofu)
{
if (vistor == NULL || tofu == NULL) {
return U_OK;
}
tofu_t *p_tofu = (tofu_t *)tofu;
vistor_t *p_vistor = (vistor_t *)vistor;
p_vistor->process(p_tofu, p_tofu);
return OK;
}
int main(void)
{
tofu_t *tf = (tofu_t *)malloc(sizeof(tofu_t));
vistor_t *vst = (vistor_t *)malloc(sizeof(vistor_t));
if (tf == NULL || vst == NULL) {
return U_OK;
}
tf->eat = eat;
tf->type = SPICY_FOOD;
vst->proces = process;
vst->region = EAST;
tf->eat(tf, vst);
free(tf);
free(vst);
}
7、观察者模式(Observer)¶
抽象出众多对象共同的属性,该属性变化后,其他属性全部跟着改变,而不需要针对每个对象依次修改。
基本知识 1、又叫发布-订阅模式。 2、两个角色:观察者和被观察对象 3、两者之间存在“观察”的逻辑关联 4、当被观察者发生改变的时候,观察者就会观察到这样的变化,并且做出相应的响应 5、“观察”不是“直接调用” 6、实现观察者模式有很多形式,比较直观的一种是使用一种“注册——通知——撤销注册”的形式。
实现步骤 1、观察者将自己注册到被观察对象中,被观察对象将观察者存放在一个容器里
2、被观察对象发生了某种变化,从容器中得到所有注册过的观察者,将变化通知观察者
3、观察者告诉被观察者要撤销观察,被观察者从容器中将观察者去除
#include "list.h"
#include "common.h"
#include <assert.h>
#define MAX_BINDING_NUMBER 8
#define LOWEND_TYPE 0x01
#define MIDEND_TYPE 0x02
#define HIGHEND_TYPE 0x03
typedef struct _Subscriber {
int type;
void (*update)(struct _Subscriber *subscriber, char *recv_msg);
} Subscriber;
typedef struct _Topic {
Subscriber *subscribers[MAX_BINDING_NUMBER];
int number;
void (*attach)(struct _Topic *topic, Subscriber *subscriber);
void (*deattach)(struct _Topic *topic, Subscriber *subscriber);
void (*notify)(struct _Topic *topic, char *msg);
} Topic;
typedef struct _Publisher {
int type;
void (*publish)(struct _Publisher *publisher, char *pub_msg, Topic *topic);
} Publisher;
void update(struct _Subscriber *subscriber, char *recv_msg)
{
printf("订阅者%d收到消息:\\\\\\\\t%s\r\n", subscriber->type, recv_msg);
}
void attach(Topic *topic, Subscriber *subscriber)
{
topic->subscribers[topic->number++] = subscriber;
}
void notify(Topic *topic, char *msg)
{
Subscriber *subscriber;
for (int i = 0; i < topic->number; i++) {
subscriber = topic->subscribers[i];
subscriber->update( subscriber, msg );
}
}
void publish(Publisher *publisher, char *pub_msg, Topic *topic)
{
printf("发布者%d发布消息:\\\\\\\\t%s\\\\\\\\n\r\n", publisher->type, pub_msg);
topic->notify( topic, pub_msg );
}
int main(void)
{
Subscriber lowend_subscriber = {LOWEND_TYPE, update};
Subscriber midend_subscriber = {MIDEND_TYPE, update};
Subscriber highend_subscriber = {HIGHEND_TYPE, update};
Topic platform_topic;
platform_topic.number = 0;
platform_topic.attach = attach;
platform_topic.notify = notify;
platform_topic.attach(&platform_topic, &lowend_subscriber);
platform_topic.attach(&platform_topic, &midend_subscriber);
platform_topic.attach(&platform_topic, &highend_subscriber);
Publisher boss;
boss.type = LOWEND_TYPE;
boss.publish = publish;
printf("\\\\\\\\n---------------------\\\\\\\\n\r\n");
boss.publish(&boss, "订阅zephyr信息", &platform_topic);
printf("\\\\\\\\n---------------------\\\\\\\\n\r\n");
boss.publish(&boss, "订阅鸿蒙OS信息", &platform_topic);
printf("\\\\\\\\n---------------------\\\\\\\\n\r\n");
boss.publish( &boss, "订阅darknet信息", &platform_topic);
printf("\\\\\\\\n---------------------\\\\\\\\n\r\n");
}
#include "common.h"
#include <urcu/urcu-memb.h> /* Userspace RCU flavor */
#include <urcu/rcuhlist.h> /* RCU hlist */
#include <urcu/compiler.h> /* For CAA_ARRAY_SIZE */
typedef struct subs {
int type;
void (*update)(struct subs *sub, void *arg);
struct cds_hlist_node node;
struct rcu_head rcu_head;
} subs_t;
typedef struct topic {
struct cds_hlist_head my_list;
void (*attach)(struct topic *topic, subs_t *sub);
void (*deattach)(struct topic *topic, subs_t *sub);
void (*notify)(struct topic *topic, void *arg);
} topic_t;
typedef struct publisher {
int type;
void (*publish)(struct publisher *pub, void *arg, topic_t *topic);
} publisher_t;
static void free_node_rcu(struct rcu_head *head)
{
subs_t *node = caa_container_of(head, subs_t, rcu_head);
free(node);
}
void deattach(struct topic *topic, subs_t *sub)
{
subs_t *node;
urcu_memb_read_lock();
cds_hlist_for_each_entry_2(node, &(topic->my_list), node) {
if (sub->type == node->type) {
cds_hlist_del_rcu(&node->node);
/*
* We can only reclaim memory after a grace
* period has passed after cds_hlist_del_rcu().
*/
urcu_memb_call_rcu(&node->rcu_head, free_node_rcu);
}
}
urcu_memb_read_unlock();
}
void attach(topic_t *topic, subs_t *sub)
{
cds_hlist_add_head_rcu(&(sub->node), &(topic->my_list));
}
void update(struct subs *sub, void *arg)
{
char *msg = (char *)arg;
printf("sub %d get msg : %s.\n", sub->type, msg);
}
void notify(topic_t *topic, void *arg)
{
subs_t *node;
char *msg = (char *)arg;
urcu_memb_read_lock();
cds_hlist_for_each_entry_2(node, &(topic->my_list), node) {
node->update(node, msg);
}
urcu_memb_read_unlock();
}
void publish(struct publisher *pub, void *arg, topic_t *topic)
{
char *msg = (char *)arg;
printf("publisher %d publish: %s.\n", pub->type, msg);
topic->notify(topic, msg);
}
int main(void)
{
urcu_memb_register_thread();
topic_t topic;
topic.attach = attach;
topic.deattach = deattach;
topic.notify = notify;
CDS_INIT_HLIST_HEAD(&topic.my_list);
subs_t *sub1 = (subs_t *)malloc(sizeof(subs_t));
if (!sub1) {
goto end;
}
sub1->type = 0x01;
sub1->update = update;
subs_t *sub2 = (subs_t *)malloc(sizeof(subs_t));
if (!sub2) {
goto end;
}
sub2->type = 0x02;
sub2->update = update;
topic.attach(&topic, sub1);
topic.attach(&topic, sub2);
publisher_t pub1;
pub1.type = 0x01;
pub1.publish = publish;
char *msg1 = "订阅zephyr信息";
char *msg2 = "订阅鸿蒙OS信息";
char *msg3 = "订阅darknet信息";
pub1.publish(&pub1, msg1, &topic);
pub1.publish(&pub1, msg2, &topic);
pub1.publish(&pub1, msg3, &topic);
printf("after deattaching.\n");
topic.deattach(&topic, sub2);
pub1.publish(&pub1, msg1, &topic);
pub1.publish(&pub1, msg2, &topic);
pub1.publish(&pub1, msg3, &topic);
urcu_memb_unregister_thread();
return 0;
end:
urcu_memb_unregister_thread();
return -1;
}
8、状态模式(State)¶
状态模式是一种行为模式,它与有限状态机的概念紧密相关;其主要思想是程序在任意时刻仅可处于几种有限的状态中。在任何一个特定状态中,程序的行为都不相同,且可瞬间从一个状态切换到另一个状态。不过,根据当前状态,程序可能会切换到另外一种状态,也可能会保持当前状态不变。这些数量有限且预先定义的状态切换规则被称为转移。
模拟灯的状态
#include <stdio.h>
/***************************************************************
* @brief:模拟开灯状态的随时间的改变
**************************************************************/
typedef enum {
STATE_LIGHT_OFF = 0X00,
STATE_LIGHT_ON = 0X01
} LIGHT_STATE;
#define STATE_MAX 2
#define EVENT_BTN_OFF 0
#define EVENT_BTN_ON 1
#define EVEN_MAX 2
int light_ctrl_state[STATE_MAX][EVEN_MAX] =
{
[STATE_LIGHT_OFF][EVENT_BTN_OFF] = STATE_LIGHT_OFF,
[STATE_LIGHT_OFF][EVENT_BTN_ON] = STATE_LIGHT_ON,
[STATE_LIGHT_ON][EVENT_BTN_OFF] = STATE_LIGHT_OFF,
[STATE_LIGHT_ON][EVENT_BTN_ON] = STATE_LIGHT_ON
};
typedef struct
{
int light_state;
int bt_event;
} STATE_INFO;
int light_state_change(STATE_INFO *state_info)
{
return light_ctrl_state[state_info->light_state][state_info->bt_event];
}
int main(void)
{
STATE_INFO info = { 0 };
info.light_state = STATE_LIGHT_OFF;
info.bt_event = EVENT_BTN_ON;
int state = light_state_change(&info);
if (state == STATE_LIGHT_OFF) {
printf("light off\n");
} else {
printf("light on\n");
}
return 0;
}
模拟BLE 链路层 轮询状态
#include "common.h"
#include <assert.h>
#include <stdint.h>
#define SUCCESS 0
#define FAIL 1
/***************************************************************
* @brief:模拟BLE 链路层 轮询状态
**************************************************************/
/*就绪态*/
static void standby_state(void)
{
printf("Standby\r\n");
}
/*扫描态*/
static void scan_state(void)
{
printf("Scan\r\n");
}
/*广播态*/
static void adv_state(void)
{
printf("Adv\r\n");
}
/*连接态*/
static void conn_state(void)
{
printf("Conn\r\n");
}
/*发起态*/
static void init_state(void)
{
printf("Init\r\n");
}
/*链路层状态*/
typedef enum {
SCAN,
STANDBY,
ADV,
CONN,
INIT,
} SM_STATE;
/*触发事件*/
typedef enum {
EVENT1,
EVENT2,
EVENT3,
EVENT4,
EVENT5,
EVENT6,
EVENT7,
EVENT8,
EVENT9,
} SM_EVENT;
/*回调*/
typedef void (*state_cb)(void);
/*状态表*/
typedef struct {
SM_EVENT event;
SM_STATE cur_state;
SM_STATE next_state;
state_cb cb;
} SM_TABLE_T;
/*状态表*/
SM_TABLE_T state_table[] = {
{EVENT1, STANDBY, SCAN, scan_state},
{EVENT2, STANDBY, ADV, adv_state},
{EVENT3, STANDBY, INIT, init_state},
{EVENT4, SCAN, STANDBY, standby_state},
{EVENT5, INIT, STANDBY, standby_state},
{EVENT6, ADV, STANDBY, standby_state},
{EVENT7, CONN, STANDBY, standby_state},
{EVENT8, ADV, CONN, conn_state},
{EVENT9, INIT, CONN, conn_state},
};
/*状态机*/
typedef struct {
SM_STATE cur_state;
SM_TABLE_T *table;
int size;
}SM_T;
int event_handle(SM_EVENT event, SM_T *sm)
{
assert(sm);
for (int i = 0; i < sm->size; i++) {
if (sm->table[i].event == event && sm->table[i].cur_state == sm->cur_state) {
sm->cur_state == sm->table[i].next_state;
sm->table[i].cb();
return SUCCESS;
}
}
return FAIL;
}
int main(void)
{
SM_T *sm = (SM_T *)malloc(sizeof(SM_T));
assert(sm);
sm->cur_state = STANDBY;
sm->table = state_table;
sm->size = (sizeof(state_table)/ sizeof(state_table[0]));
SM_EVENT input_event[] = {EVENT1, EVENT2, EVENT3, EVENT4, EVENT5,
EVENT6, EVENT7, EVENT8, EVENT9};
for (int i = 0; i < sm->size; ++i) {
event_handle(input_event[i], sm);
}
free(sm);
}
9、策略模式(Strategy)¶
在软件配置阶段,根据开关状态的不同,我们给一个接口赋一个具体实现,在软件运行阶段,仅调用接口。
#include "common.h"
typedef void (*ProcStrategy)(void);
void proc_strategy1(void)
{
printf("proc_strategy1\r\n");
}
void proc_strategy2( void )
{
printf("proc_strategy2\r\n");
}
#define LOWEND_TYPE 0x01
#define HIGHEND_TYPE 0x02
int main(void)
{
int strategy = LOWEND_TYPE;
ProcStrategy proc_strategy;
if (strategy == LOWEND_TYPE) {
proc_strategy = proc_strategy1;
} else {
proc_strategy = proc_strategy2;
}
//核心逻辑处理过程
proc_strategy();
}
10、模板方法模式(Template Method)¶
介绍 模板方法模式是基于继承的代码复用基本技术,可以将相同的代码放在父类中,而将不同的方法实现放在不同的子类中。 在模板方法模式中,准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法来让子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。模板方法模式体现了面向对象的诸多重要思想,是一种使用频率较高的模式。
比方说,现在我们需要设计一个流程。这个流程有很多小的步骤完成。然而,其中每一个步骤的方法是多种多样的,我们可以很多选择。但是,所有步骤构成的逻辑是唯一的。上
#include "common.h"
#include <stdint.h>
typedef struct basic
{
//具体每个流程的步骤
void (*step1)(void);
void (*step2)(void);
void (*step3)(void);
void (*step4)(void);
//最终执行函数的方法
void (*process)(void *);
} basic_t;
//计算偏置电压
void step1(void)
{
printf(" VIB STEP1:calculate_bias_voltage !\n");
}
//当前采集时间
void step2(void)
{
printf(" VIB STEP2:rz_time_get(ptimebuf) !\n");
}
//上传波形
void step3(void)
{
printf(" VIB STEP3:upload vib_bias_vol data !\n");
}
void process(void *basic)
{
if (basic == NULL) {
return;
}
basic_t *data = (basic_t *)basic;
data->step1();
data->step2();
data->step3();
}
void vib_init(struct basic* pBasic)
{
pBasic->step1 = step1;
pBasic->step2 = step2;
pBasic->step3 = step3;
pBasic->process = process;
}
int main(void)
{
basic_t step = { 0 };
vib_init(&step);
step.process(&step);
}
11、备忘录模式(memo mode)¶
代码设计在修改之后支持回退(撤销、恢复)
#include "common.h"
typedef struct action {
int type;
struct action *next;
void *pData;
void (*process)(void *);
} action_t;
typedef struct organizer {
int number;
action_t *paction_tHead;
action_t *(*create)(void);
void (*restore)(struct organizer*);
} organizer_t;
void restore(struct organizer* organizer)
{
action_t *pHead;
assert(NULL != organizer);
pHead = organizer->paction_tHead;
pHead->process(pHead->pData);
organizer->paction_tHead = pHead->next;
organizer->number--;
free(pHead);
return;
}
void process(void *data)
{
}
int main(void)
{
}
事件处理的策略¶
嵌入式软件都是接收到一个事件,然后处理。如按键——点灯的操作,便是事件——处理。
对于代码可以这样表示
static void LedOn() \\点亮一个LED
{
...
}
static int isKeyDown() \\检测按键是否按下
{
...
}
int main()
{
while(1) {
//事件检测
if(isKeyDown()) {
LedOn(); //事件处理
}
}
}
如果加入一个高温预警功能呢
static void LedOn() \\点亮一个LED
{
...
}
static int isKeyDown() \\检测按键是否按下
{
...
}
static void Alarm() \\预警
{
...
}
static int isTemperatureHigh()检测温度是否过高
{
...
}
int main()
{
while(1){
if(isKeyDown()) //事件检测
LedOn(); //事件处理
if(isTemperatureHigh()) //事件检测
Alarm(); //事件处理
}
}
可以看到,当处理的情景越来越多,main函数将会不堪重负,因此有了解决方案。
typedef void (*eventHandler)(); //函数指针的声明
static int EVENT = 0; //int为16bit
static int firstBitEqualOne(int event) //返回event中第一个等于1的bit位
{
...
}
static void setBit(int event,int pos) //将event的第pos位置1
{
....
}
void keyDownEvent() //在中断中实现
{
if(isKeyDown())
setBit(EVENT,KEYDONW); //设置事件
}
static void LedOn() \\点亮一个LED
{
...
}
#define KEYDOWN 3 //按键按下的事件为3
static eventHandler[16] = {NULL, NULL, NULL, LedOn, NULL ....};//LedOn的数组下标为3
eventHandler findEventHandler(int event) //根据event编号,寻找eventHandler
{
return eventHandler[event];
}
int Event()
{
if (EVENT != 0)
return firstBitEqualOne(EVENT) //有事件产生,返回事件编号
return -1; //没有事件产生,返回-1
}
int main()
{
int event;
eventHandler func;
while(1) {
event = Event(); //事件检测,可以检测到按键按下
if (event != -1){
func = findEventHandler(event); //事件匹配,可以匹配到LedOn函数
func(); //事件处理,调用LedOn函数
}
}
}