Skip to content

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)

工作原理

  1. 将一个模块的接口转换成另一种接口,转换后的接口可以兼容。
  2. 调用者(用户)不需要知道各模块内部的实现,方便增减模块,可以解耦,方便移植

在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字节

传感器数据格式定义如下

传感器 类型号 数据格式
温度传感器 整型数据(4字节)
湿度传感器 整型数据(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函数
        }
    }
}