【C语言】“面向对象”:API的结构体封装


1. 引言

  • 在嵌入式开发中,我们常会遇到这样的需求:
    • 同一款产品可能会使用不同型号的传感器(比如 AHT 20 或 SHT 30),希望在编译甚至运行时切换驱动。
    • 系统需要同时支持多个同类型设备(例如两个温湿度传感器),但上层逻辑希望统一调用。
  • 这些场景都指向同一个设计需求:上层代码依赖“接口”而非具体实现,而这就是面向对象中多态解决的问题。
  • 虽然 C 语言没有 class 和 interface 关键字,但我们可以用结构体封装函数指针来模拟多态,从而实现底层驱动与上层应用的解耦。

2. 以温湿度传感器的例子 :AHT20、SHT30

2.1 公共层:创建一个 api_typedef.h

  • 该头文件用于存放各类传感器的 API 结构体定义
  • 代码如下:
#ifndef __API_TYPEDEF_H
#define __API_TYPEDEF_H

#include <stdint.h>

/* 温湿度类传感器API结构体定义 */
typedef struct
{
    uint8_t (*Init)(void);  //初始化
    uint8_t (*GetData)(float* p_temp,float* p_humi);//获取数据
}SensorWS_Driver_t;

/* 其它类外设API结构体定义 */
/* ... */

#endif

2.2 驱动层:编写 AHT20 、SHT30底层驱动

2.2.1 AHT20 底层驱动

  • 创建 dev_aht20.h 和 dev_aht20.c
  • dev_aht20.h 内容如下:
#ifndef __DEV_AHT20_H
#define __DEV_AHT20_H

/***
* @brief 初始化AHT20
* @return 1为初始化成功,0为初始化失败
*/
uint8_t Dev_AHT20_Init(void);

/***
* @brief 读取AHT20的温湿度数据
* @param p_temp 以指针形式返回的温度数据
* @param p_humi 以指针形式返回的湿度数据
* @return 1为读取数据成功,0为读取数据失败
*/
uint8_t Dev_AHT20_GetData(float* p_temp,float* p_humi);

#endif
  • dev_aht20.c 内容:略

2.2.2 SHT30 底层驱动

  • 创建 dev_sht30.h 和 dev_sht30.c
  • dev_sht30.h 内容如下:
#ifndef __DEV_SHT30_H
#define __DEV_SHT30_H

/***
* @brief 初始化SHT30
* @return 1为初始化成功,0为初始化失败
*/
uint8_t Dev_SHT30_Init(void);

/***
* @brief 读取SHT30的温湿度数据
* @param p_temp 以指针形式返回的温度数据
* @param p_humi 以指针形式返回的湿度数据
* @return 1为读取数据成功,0为读取数据失败
*/
uint8_t Dev_SHT30_GetData(float* p_temp,float* p_humi);

#endif
  • dev_sht30.c 内容:略

2.3 main.c 使用案例

#include <stdio.h>
#include <stdint.h>
#include "api_typedef.h"
#include "dev_aht20.h"
#include "dev_sht30.h"

#define USE_SENSOR_WS_NUM 2

static SensorWS_Driver_t AHT20 = {
    .Init = Dev_AHT20_Init,
    .GetData = Dev_AHT20_GetData
};

static SensorWS_Driver_t SHT30 = {
    .Init = Dev_SHT30_Init,
    .GetData = Dev_SHT30_GetData
};

static SensorWS_Driver_t* pDev[USE_SENSOR_WS_NUM] = { &AHT20, &SHT30 };

int main()
{
    for (uint8_t i = 0; i < USE_SENSOR_WS_NUM; i++)
    {
        if (pDev[i]->Init() != 1)
        {
            // 初始化失败处理
        }
    }

    while (1)
    {
        float wendu = 0.0f;
        float shidu = 0.0f;

        for (uint8_t i = 0; i < USE_SENSOR_WS_NUM; i++)
        {
            if (pDev[i]->GetData(&wendu, &shidu) == 1)
            {
                // 实际使用时请替换为嵌入式串口打印函数
                printf("温度:%.1f,湿度:%.1f \n", wendu, shidu);
            }
        }
    }
}

3. 总结

  • 这样,我们就可以轻松更改底层驱动,而不影响相关业务的实现
  • 如果只是单纯为了方便随时更换底层驱动,对于资源极度紧张的场景,也可以使用宏来实现静态接口,但本文重点展示多态设计

posted @ 2026-04-12 14:11  临祁  阅读(3)  评论(0)    收藏  举报