跳转至

面向接口编程实践教程

学习目标

完成本教程后,你将能够:

  • 理解面向接口编程的核心概念和设计思想
  • 掌握在C语言中实现接口抽象的多种方法
  • 学会使用依赖注入提高代码的灵活性和可测试性
  • 能够设计清晰的抽象层,实现模块间的解耦
  • 理解接口设计如何提高代码的可维护性和可扩展性

前置要求

在开始本教程之前,你需要:

知识要求: - 熟练掌握C语言编程 - 理解函数指针和回调函数的概念 - 了解结构体和指针的高级用法 - 掌握基本的软件设计原则

技能要求: - 能够设计和实现模块化的代码结构 - 具备面向对象的设计思维 - 会使用IDE进行代码重构 - 理解分层架构的概念

环境要求: - 已完成基础设计模式的学习 - 具备一定的嵌入式项目开发经验

准备工作

硬件准备

名称 数量 说明 参考链接
STM32开发板 1 STM32F103或更高系列 -
传感器模块 2-3 温湿度、光照等不同类型 -
OLED显示屏 1 128x64 I2C接口 -

软件准备

  • 开发环境:STM32CubeIDE 或 Keil MDK
  • 编译器:ARM GCC
  • 调试工具:ST-Link
  • 版本控制:Git(推荐)

环境配置

  1. 安装并配置开发环境
  2. 创建新的STM32项目
  3. 配置GPIO、I2C等外设
  4. 准备好串口调试工具

第一部分:面向接口编程概述

1.1 什么是面向接口编程

面向接口编程(Interface-Oriented Programming)是一种软件设计思想,强调通过定义清晰的接口来实现模块间的交互,而不是直接依赖具体的实现。

核心思想: - 依赖于抽象,而不是具体实现 - 通过接口定义契约,规范模块间的交互 - 实现与接口分离,提高代码的灵活性 - 面向接口编程,而不是面向实现编程

与面向对象编程的关系: - 面向接口编程是面向对象设计原则的体现 - 在C语言中,通过函数指针和结构体实现接口抽象 - 强调"依赖倒置原则":高层模块不应依赖低层模块,两者都应依赖抽象

1.2 为什么需要面向接口编程

传统方式的问题

// 传统方式:直接依赖具体实现
#include "dht11_sensor.h"  // 直接依赖DHT11传感器

void readTemperature(void) {
    float temp = dht11_readTemperature();  // 直接调用DHT11的函数
    printf("Temperature: %.1f°C\n", temp);
}

问题分析: 1. 强耦合:代码直接依赖DHT11传感器,更换传感器需要修改代码 2. 难以测试:无法在没有硬件的情况下测试代码 3. 扩展性差:添加新传感器需要修改现有代码 4. 维护困难:传感器变更影响所有使用它的代码

面向接口的方式

// 面向接口:依赖抽象接口
typedef struct {
    float (*readTemperature)(void);  // 温度读取接口
    float (*readHumidity)(void);     // 湿度读取接口
} SensorInterface;

void readTemperature(SensorInterface* sensor) {
    if (sensor && sensor->readTemperature) {
        float temp = sensor->readTemperature();  // 通过接口调用
        printf("Temperature: %.1f°C\n", temp);
    }
}

优势: 1. 低耦合:代码只依赖接口,不依赖具体实现 2. 易测试:可以创建模拟对象进行测试 3. 易扩展:添加新传感器只需实现接口 4. 易维护:传感器变更不影响使用代码

1.3 面向接口编程的核心原则

1. 依赖倒置原则(Dependency Inversion Principle) - 高层模块不应依赖低层模块,两者都应依赖抽象 - 抽象不应依赖细节,细节应依赖抽象

2. 接口隔离原则(Interface Segregation Principle) - 客户端不应被迫依赖它不使用的接口 - 接口应该小而专注,而不是大而全

3. 里氏替换原则(Liskov Substitution Principle) - 子类型必须能够替换其基类型 - 所有实现同一接口的对象应该可以互相替换


第二部分:C语言中的接口实现

2.1 使用函数指针实现接口

在C语言中,我们使用函数指针来模拟接口的概念。

2.1.1 基础接口定义

// sensor_interface.h
#ifndef SENSOR_INTERFACE_H
#define SENSOR_INTERFACE_H

#include <stdint.h>
#include <stdbool.h>

// 传感器接口定义
typedef struct {
    // 初始化函数
    bool (*init)(void);

    // 读取温度(返回摄氏度)
    float (*readTemperature)(void);

    // 读取湿度(返回百分比)
    float (*readHumidity)(void);

    // 检查传感器是否就绪
    bool (*isReady)(void);

    // 传感器名称
    const char* name;
} SensorInterface;

#endif // SENSOR_INTERFACE_H

设计要点: - 使用函数指针定义接口方法 - 包含必要的元数据(如name) - 返回值和参数类型要清晰明确 - 考虑错误处理(返回bool或错误码)

2.1.2 具体实现:DHT11传感器

// dht11_sensor.c
#include "sensor_interface.h"
#include <stdio.h>

// DHT11传感器的私有数据
static float lastTemperature = 0.0f;
static float lastHumidity = 0.0f;
static bool initialized = false;

// DHT11传感器的具体实现
static bool dht11_init(void) {
    printf("[DHT11] 初始化传感器...\n");
    // 实际的硬件初始化代码
    initialized = true;
    return true;
}

static float dht11_readTemperature(void) {
    if (!initialized) {
        printf("[DHT11] 错误:传感器未初始化\n");
        return 0.0f;
    }

    // 模拟读取温度
    lastTemperature = 25.5f;  // 实际应从硬件读取
    printf("[DHT11] 读取温度: %.1f°C\n", lastTemperature);
    return lastTemperature;
}

static float dht11_readHumidity(void) {
    if (!initialized) {
        printf("[DHT11] 错误:传感器未初始化\n");
        return 0.0f;
    }

    // 模拟读取湿度
    lastHumidity = 60.0f;  // 实际应从硬件读取
    printf("[DHT11] 读取湿度: %.1f%%\n", lastHumidity);
    return lastHumidity;
}

static bool dht11_isReady(void) {
    return initialized;
}

// 创建DHT11传感器接口对象
SensorInterface dht11Sensor = {
    .init = dht11_init,
    .readTemperature = dht11_readTemperature,
    .readHumidity = dht11_readHumidity,
    .isReady = dht11_isReady,
    .name = "DHT11"
};

2.1.3 具体实现:DS18B20传感器

// ds18b20_sensor.c
#include "sensor_interface.h"
#include <stdio.h>

// DS18B20传感器的私有数据
static float lastTemperature = 0.0f;
static bool initialized = false;

// DS18B20传感器的具体实现
static bool ds18b20_init(void) {
    printf("[DS18B20] 初始化传感器...\n");
    // 实际的硬件初始化代码
    initialized = true;
    return true;
}

static float ds18b20_readTemperature(void) {
    if (!initialized) {
        printf("[DS18B20] 错误:传感器未初始化\n");
        return 0.0f;
    }

    // 模拟读取温度(DS18B20精度更高)
    lastTemperature = 25.75f;  // 实际应从硬件读取
    printf("[DS18B20] 读取温度: %.2f°C\n", lastTemperature);
    return lastTemperature;
}

static float ds18b20_readHumidity(void) {
    // DS18B20不支持湿度读取
    printf("[DS18B20] 警告:不支持湿度读取\n");
    return 0.0f;
}

static bool ds18b20_isReady(void) {
    return initialized;
}

// 创建DS18B20传感器接口对象
SensorInterface ds18b20Sensor = {
    .init = ds18b20_init,
    .readTemperature = ds18b20_readTemperature,
    .readHumidity = ds18b20_readHumidity,
    .isReady = ds18b20_isReady,
    .name = "DS18B20"
};

2.1.4 使用接口的客户端代码

// temperature_monitor.c
#include "sensor_interface.h"
#include <stdio.h>

// 温度监控器:依赖传感器接口,而不是具体实现
typedef struct {
    SensorInterface* sensor;  // 依赖抽象接口
    float threshold;          // 温度阈值
} TemperatureMonitor;

// 初始化监控器
void monitor_init(TemperatureMonitor* monitor, SensorInterface* sensor, float threshold) {
    monitor->sensor = sensor;
    monitor->threshold = threshold;

    if (sensor && sensor->init) {
        sensor->init();
    }
}

// 检查温度
void monitor_check(TemperatureMonitor* monitor) {
    if (!monitor->sensor) {
        printf("[监控器] 错误:未设置传感器\n");
        return;
    }

    // 通过接口读取温度
    float temp = monitor->sensor->readTemperature();

    printf("[监控器] 当前温度: %.1f°C (阈值: %.1f°C)\n", temp, monitor->threshold);

    if (temp > monitor->threshold) {
        printf("[监控器] 警告:温度超过阈值!\n");
    }
}

// 切换传感器(运行时替换)
void monitor_setSensor(TemperatureMonitor* monitor, SensorInterface* sensor) {
    printf("[监控器] 切换传感器: %s\n", sensor->name);
    monitor->sensor = sensor;

    if (sensor && sensor->init) {
        sensor->init();
    }
}

2.1.5 使用示例

// main.c
#include "sensor_interface.h"
#include "temperature_monitor.h"
#include <stdio.h>

// 外部传感器对象声明
extern SensorInterface dht11Sensor;
extern SensorInterface ds18b20Sensor;

int main(void) {
    printf("=== 面向接口编程示例:温度监控系统 ===\n\n");

    // 创建温度监控器
    TemperatureMonitor monitor;

    // 场景1:使用DHT11传感器
    printf("场景1:使用DHT11传感器\n");
    monitor_init(&monitor, &dht11Sensor, 26.0f);
    monitor_check(&monitor);
    printf("\n");

    // 场景2:运行时切换到DS18B20传感器
    printf("场景2:切换到DS18B20传感器\n");
    monitor_setSensor(&monitor, &ds18b20Sensor);
    monitor_check(&monitor);
    printf("\n");

    // 场景3:再次切换回DHT11
    printf("场景3:切换回DHT11传感器\n");
    monitor_setSensor(&monitor, &dht11Sensor);
    monitor_check(&monitor);

    return 0;
}

运行结果

=== 面向接口编程示例:温度监控系统 ===

场景1:使用DHT11传感器
[DHT11] 初始化传感器...
[DHT11] 读取温度: 25.5°C
[监控器] 当前温度: 25.5°C (阈值: 26.0°C)

场景2:切换到DS18B20传感器
[监控器] 切换传感器: DS18B20
[DS18B20] 初始化传感器...
[DS18B20] 读取温度: 25.75°C
[监控器] 当前温度: 25.75°C (阈值: 26.0°C)

场景3:切换回DHT11传感器
[监控器] 切换传感器: DHT11
[DHT11] 初始化传感器...
[DHT11] 读取温度: 25.5°C
[监控器] 当前温度: 25.5°C (阈值: 26.0°C)

2.2 接口设计的最佳实践

2.2.1 接口粒度控制

原则:接口应该小而专注,遵循单一职责原则。

不好的设计

// 接口过大,包含太多职责
typedef struct {
    bool (*init)(void);
    float (*readTemperature)(void);
    float (*readHumidity)(void);
    float (*readPressure)(void);
    float (*readLight)(void);
    void (*calibrate)(void);
    void (*reset)(void);
    void (*sleep)(void);
    void (*wakeup)(void);
} SensorInterface;  // 接口太大,不是所有传感器都需要这些功能

好的设计

// 基础传感器接口
typedef struct {
    bool (*init)(void);
    bool (*isReady)(void);
    const char* name;
} BaseSensorInterface;

// 温度传感器接口
typedef struct {
    BaseSensorInterface base;
    float (*readTemperature)(void);
} TemperatureSensorInterface;

// 湿度传感器接口
typedef struct {
    BaseSensorInterface base;
    float (*readHumidity)(void);
} HumiditySensorInterface;

// 组合接口:温湿度传感器
typedef struct {
    BaseSensorInterface base;
    float (*readTemperature)(void);
    float (*readHumidity)(void);
} TempHumiditySensorInterface;

2.2.2 接口命名规范

原则:接口名称应该清晰表达其职责和用途。

// 好的命名
typedef struct {
    void (*read)(void* buffer, size_t size);
    void (*write)(const void* buffer, size_t size);
} StorageInterface;

typedef struct {
    bool (*send)(const uint8_t* data, size_t len);
    bool (*receive)(uint8_t* buffer, size_t* len);
} CommunicationInterface;

typedef struct {
    void (*on)(void);
    void (*off)(void);
    void (*toggle)(void);
} SwitchableInterface;

2.2.3 错误处理设计

原则:接口应该提供清晰的错误处理机制。

// 方式1:使用返回值表示成功/失败
typedef struct {
    bool (*init)(void);                    // 返回true表示成功
    bool (*read)(float* value);            // 通过指针返回数据,返回值表示成功/失败
} SensorInterface_v1;

// 方式2:使用错误码
typedef enum {
    SENSOR_OK = 0,
    SENSOR_ERROR_NOT_INIT,
    SENSOR_ERROR_TIMEOUT,
    SENSOR_ERROR_INVALID_DATA,
    SENSOR_ERROR_HARDWARE
} SensorError;

typedef struct {
    SensorError (*init)(void);
    SensorError (*read)(float* value);
} SensorInterface_v2;

// 方式3:使用回调函数报告错误
typedef void (*ErrorCallback)(SensorError error, const char* message);

typedef struct {
    void (*setErrorCallback)(ErrorCallback callback);
    bool (*init)(void);
    float (*read)(void);  // 错误通过回调报告
} SensorInterface_v3;

第三部分:依赖注入

3.1 什么是依赖注入

依赖注入(Dependency Injection, DI)是一种实现控制反转(Inversion of Control, IoC)的技术,通过外部注入依赖对象,而不是在内部创建。

传统方式

// 内部创建依赖(紧耦合)
void temperatureMonitor_init(TemperatureMonitor* monitor) {
    monitor->sensor = &dht11Sensor;  // 硬编码依赖DHT11
}

依赖注入方式

// 通过参数注入依赖(松耦合)
void temperatureMonitor_init(TemperatureMonitor* monitor, SensorInterface* sensor) {
    monitor->sensor = sensor;  // 依赖从外部注入
}

3.2 依赖注入的三种方式

3.2.1 构造函数注入(初始化时注入)

// 通过初始化函数注入依赖
typedef struct {
    SensorInterface* sensor;
    DisplayInterface* display;
    float threshold;
} TemperatureController;

void controller_init(TemperatureController* ctrl,
                    SensorInterface* sensor,
                    DisplayInterface* display,
                    float threshold) {
    ctrl->sensor = sensor;      // 注入传感器依赖
    ctrl->display = display;    // 注入显示器依赖
    ctrl->threshold = threshold;
}

优点: - 依赖关系明确 - 对象创建后即可使用 - 强制提供所有必需的依赖

缺点: - 参数较多时不够灵活 - 依赖变更需要修改初始化函数

3.2.2 Setter注入(设置器注入)

// 通过setter函数注入依赖
typedef struct {
    SensorInterface* sensor;
    DisplayInterface* display;
    float threshold;
} TemperatureController;

void controller_setSensor(TemperatureController* ctrl, SensorInterface* sensor) {
    ctrl->sensor = sensor;
}

void controller_setDisplay(TemperatureController* ctrl, DisplayInterface* display) {
    ctrl->display = display;
}

void controller_setThreshold(TemperatureController* ctrl, float threshold) {
    ctrl->threshold = threshold;
}

优点: - 灵活,可以单独设置每个依赖 - 可以在运行时更换依赖 - 参数列表简洁

缺点: - 对象可能处于不完整状态 - 需要检查依赖是否已设置

3.2.3 接口注入(通过接口方法注入)

// 定义注入接口
typedef struct {
    void (*injectSensor)(void* obj, SensorInterface* sensor);
    void (*injectDisplay)(void* obj, DisplayInterface* display);
} DependencyInjector;

// 实现注入接口
static void controller_injectSensor(void* obj, SensorInterface* sensor) {
    TemperatureController* ctrl = (TemperatureController*)obj;
    ctrl->sensor = sensor;
}

static void controller_injectDisplay(void* obj, DisplayInterface* display) {
    TemperatureController* ctrl = (TemperatureController*)obj;
    ctrl->display = display;
}

// 创建注入器
DependencyInjector controllerInjector = {
    .injectSensor = controller_injectSensor,
    .injectDisplay = controller_injectDisplay
};

优点: - 统一的注入接口 - 便于实现依赖注入容器 - 支持复杂的注入逻辑

缺点: - 实现较复杂 - 在嵌入式系统中较少使用

3.3 依赖注入的实际应用

3.3.1 完整示例:智能温控系统

// interfaces.h - 定义所有接口
#ifndef INTERFACES_H
#define INTERFACES_H

#include <stdint.h>
#include <stdbool.h>

// 传感器接口
typedef struct {
    bool (*init)(void);
    float (*read)(void);
    const char* name;
} SensorInterface;

// 显示器接口
typedef struct {
    bool (*init)(void);
    void (*clear)(void);
    void (*print)(const char* text);
    void (*printFloat)(const char* label, float value);
    const char* name;
} DisplayInterface;

// 执行器接口(加热器、风扇等)
typedef struct {
    bool (*init)(void);
    void (*on)(void);
    void (*off)(void);
    void (*setPower)(uint8_t power);  // 0-100%
    const char* name;
} ActuatorInterface;

#endif // INTERFACES_H
// temperature_controller.h
#ifndef TEMPERATURE_CONTROLLER_H
#define TEMPERATURE_CONTROLLER_H

#include "interfaces.h"

typedef struct {
    // 依赖的接口
    SensorInterface* tempSensor;
    DisplayInterface* display;
    ActuatorInterface* heater;
    ActuatorInterface* cooler;

    // 控制参数
    float targetTemp;
    float hysteresis;  // 温度滞后范围

    // 状态
    bool isRunning;
} TemperatureController;

// 使用构造函数注入
void controller_init(TemperatureController* ctrl,
                    SensorInterface* tempSensor,
                    DisplayInterface* display,
                    ActuatorInterface* heater,
                    ActuatorInterface* cooler,
                    float targetTemp,
                    float hysteresis);

// 使用Setter注入(可选,用于运行时更换组件)
void controller_setSensor(TemperatureController* ctrl, SensorInterface* sensor);
void controller_setDisplay(TemperatureController* ctrl, DisplayInterface* display);

// 控制逻辑
void controller_start(TemperatureController* ctrl);
void controller_stop(TemperatureController* ctrl);
void controller_update(TemperatureController* ctrl);

#endif // TEMPERATURE_CONTROLLER_H
// temperature_controller.c
#include "temperature_controller.h"
#include <stdio.h>

void controller_init(TemperatureController* ctrl,
                    SensorInterface* tempSensor,
                    DisplayInterface* display,
                    ActuatorInterface* heater,
                    ActuatorInterface* cooler,
                    float targetTemp,
                    float hysteresis) {
    // 注入所有依赖
    ctrl->tempSensor = tempSensor;
    ctrl->display = display;
    ctrl->heater = heater;
    ctrl->cooler = cooler;

    // 设置参数
    ctrl->targetTemp = targetTemp;
    ctrl->hysteresis = hysteresis;
    ctrl->isRunning = false;

    // 初始化所有组件
    if (tempSensor && tempSensor->init) {
        tempSensor->init();
    }
    if (display && display->init) {
        display->init();
    }
    if (heater && heater->init) {
        heater->init();
    }
    if (cooler && cooler->init) {
        cooler->init();
    }
}

void controller_setSensor(TemperatureController* ctrl, SensorInterface* sensor) {
    printf("[控制器] 更换传感器: %s\n", sensor->name);
    ctrl->tempSensor = sensor;
    if (sensor && sensor->init) {
        sensor->init();
    }
}

void controller_setDisplay(TemperatureController* ctrl, DisplayInterface* display) {
    printf("[控制器] 更换显示器: %s\n", display->name);
    ctrl->display = display;
    if (display && display->init) {
        display->init();
    }
}

void controller_start(TemperatureController* ctrl) {
    ctrl->isRunning = true;
    printf("[控制器] 启动温度控制\n");

    if (ctrl->display) {
        ctrl->display->clear();
        ctrl->display->print("温控系统启动");
    }
}

void controller_stop(TemperatureController* ctrl) {
    ctrl->isRunning = false;
    printf("[控制器] 停止温度控制\n");

    // 关闭所有执行器
    if (ctrl->heater) {
        ctrl->heater->off();
    }
    if (ctrl->cooler) {
        ctrl->cooler->off();
    }
}

void controller_update(TemperatureController* ctrl) {
    if (!ctrl->isRunning || !ctrl->tempSensor) {
        return;
    }

    // 读取当前温度
    float currentTemp = ctrl->tempSensor->read();

    // 显示温度
    if (ctrl->display) {
        ctrl->display->printFloat("当前温度", currentTemp);
        ctrl->display->printFloat("目标温度", ctrl->targetTemp);
    }

    // 温度控制逻辑
    float diff = ctrl->targetTemp - currentTemp;

    if (diff > ctrl->hysteresis) {
        // 需要加热
        if (ctrl->heater) {
            uint8_t power = (uint8_t)(diff * 20);  // 简单的比例控制
            if (power > 100) power = 100;
            ctrl->heater->setPower(power);
            ctrl->heater->on();
        }
        if (ctrl->cooler) {
            ctrl->cooler->off();
        }
        printf("[控制器] 加热中... (温差: %.1f°C)\n", diff);
    } else if (diff < -ctrl->hysteresis) {
        // 需要制冷
        if (ctrl->cooler) {
            uint8_t power = (uint8_t)(-diff * 20);
            if (power > 100) power = 100;
            ctrl->cooler->setPower(power);
            ctrl->cooler->on();
        }
        if (ctrl->heater) {
            ctrl->heater->off();
        }
        printf("[控制器] 制冷中... (温差: %.1f°C)\n", diff);
    } else {
        // 温度合适,关闭执行器
        if (ctrl->heater) {
            ctrl->heater->off();
        }
        if (ctrl->cooler) {
            ctrl->cooler->off();
        }
        printf("[控制器] 温度正常\n");
    }
}

3.3.2 具体实现:传感器

// dht11_impl.c - DHT11传感器实现
#include "interfaces.h"
#include <stdio.h>

static bool dht11_init(void) {
    printf("[DHT11] 初始化\n");
    return true;
}

static float dht11_read(void) {
    float temp = 25.5f;  // 模拟读取
    printf("[DHT11] 读取温度: %.1f°C\n", temp);
    return temp;
}

SensorInterface dht11Sensor = {
    .init = dht11_init,
    .read = dht11_read,
    .name = "DHT11"
};

// ds18b20_impl.c - DS18B20传感器实现
static bool ds18b20_init(void) {
    printf("[DS18B20] 初始化\n");
    return true;
}

static float ds18b20_read(void) {
    float temp = 25.75f;  // 模拟读取
    printf("[DS18B20] 读取温度: %.2f°C\n", temp);
    return temp;
}

SensorInterface ds18b20Sensor = {
    .init = ds18b20_init,
    .read = ds18b20_read,
    .name = "DS18B20"
};

3.3.3 具体实现:显示器

// oled_display_impl.c - OLED显示器实现
#include "interfaces.h"
#include <stdio.h>

static bool oled_init(void) {
    printf("[OLED] 初始化显示器\n");
    return true;
}

static void oled_clear(void) {
    printf("[OLED] 清屏\n");
}

static void oled_print(const char* text) {
    printf("[OLED] 显示: %s\n", text);
}

static void oled_printFloat(const char* label, float value) {
    printf("[OLED] %s: %.1f\n", label, value);
}

DisplayInterface oledDisplay = {
    .init = oled_init,
    .clear = oled_clear,
    .print = oled_print,
    .printFloat = oled_printFloat,
    .name = "OLED"
};

// lcd_display_impl.c - LCD显示器实现
static bool lcd_init(void) {
    printf("[LCD] 初始化显示器\n");
    return true;
}

static void lcd_clear(void) {
    printf("[LCD] 清屏\n");
}

static void lcd_print(const char* text) {
    printf("[LCD] 显示: %s\n", text);
}

static void lcd_printFloat(const char* label, float value) {
    printf("[LCD] %s: %.2f\n", label, value);
}

DisplayInterface lcdDisplay = {
    .init = lcd_init,
    .clear = lcd_clear,
    .print = lcd_print,
    .printFloat = lcd_printFloat,
    .name = "LCD"
};

3.3.4 具体实现:执行器

// heater_impl.c - 加热器实现
#include "interfaces.h"
#include <stdio.h>

static uint8_t heaterPower = 0;

static bool heater_init(void) {
    printf("[加热器] 初始化\n");
    return true;
}

static void heater_on(void) {
    printf("[加热器] 开启 (功率: %d%%)\n", heaterPower);
}

static void heater_off(void) {
    printf("[加热器] 关闭\n");
}

static void heater_setPower(uint8_t power) {
    heaterPower = power;
}

ActuatorInterface heater = {
    .init = heater_init,
    .on = heater_on,
    .off = heater_off,
    .setPower = heater_setPower,
    .name = "加热器"
};

// fan_impl.c - 风扇实现
static uint8_t fanPower = 0;

static bool fan_init(void) {
    printf("[风扇] 初始化\n");
    return true;
}

static void fan_on(void) {
    printf("[风扇] 开启 (功率: %d%%)\n", fanPower);
}

static void fan_off(void) {
    printf("[风扇] 关闭\n");
}

static void fan_setPower(uint8_t power) {
    fanPower = power;
}

ActuatorInterface fan = {
    .init = fan_init,
    .on = fan_on,
    .off = fan_off,
    .setPower = fan_setPower,
    .name = "风扇"
};

3.3.5 主程序:组装和使用

// main.c
#include "temperature_controller.h"
#include <stdio.h>

// 外部对象声明
extern SensorInterface dht11Sensor;
extern SensorInterface ds18b20Sensor;
extern DisplayInterface oledDisplay;
extern DisplayInterface lcdDisplay;
extern ActuatorInterface heater;
extern ActuatorInterface fan;

int main(void) {
    printf("=== 依赖注入示例:智能温控系统 ===\n\n");

    // 创建控制器
    TemperatureController controller;

    // 场景1:使用DHT11 + OLED的配置
    printf("场景1:DHT11传感器 + OLED显示器\n");
    controller_init(&controller,
                   &dht11Sensor,    // 注入DHT11传感器
                   &oledDisplay,    // 注入OLED显示器
                   &heater,         // 注入加热器
                   &fan,            // 注入风扇
                   26.0f,           // 目标温度
                   0.5f);           // 温度滞后

    controller_start(&controller);

    // 模拟几次温度更新
    for (int i = 0; i < 3; i++) {
        printf("\n--- 更新 %d ---\n", i + 1);
        controller_update(&controller);
    }

    printf("\n");

    // 场景2:运行时更换传感器和显示器
    printf("场景2:切换到DS18B20传感器 + LCD显示器\n");
    controller_setSensor(&controller, &ds18b20Sensor);
    controller_setDisplay(&controller, &lcdDisplay);

    // 再次更新
    printf("\n--- 更新 ---\n");
    controller_update(&controller);

    printf("\n");
    controller_stop(&controller);

    return 0;
}

运行结果

=== 依赖注入示例:智能温控系统 ===

场景1:DHT11传感器 + OLED显示器
[DHT11] 初始化
[OLED] 初始化显示器
[加热器] 初始化
[风扇] 初始化
[控制器] 启动温度控制
[OLED] 清屏
[OLED] 显示: 温控系统启动

--- 更新 1 ---
[DHT11] 读取温度: 25.5°C
[OLED] 当前温度: 25.5
[OLED] 目标温度: 26.0
[加热器] 开启 (功率: 10%)
[控制器] 加热中... (温差: 0.5°C)

--- 更新 2 ---
[DHT11] 读取温度: 25.5°C
[OLED] 当前温度: 25.5
[OLED] 目标温度: 26.0
[加热器] 开启 (功率: 10%)
[控制器] 加热中... (温差: 0.5°C)

--- 更新 3 ---
[DHT11] 读取温度: 25.5°C
[OLED] 当前温度: 25.5
[OLED] 目标温度: 26.0
[加热器] 开启 (功率: 10%)
[控制器] 加热中... (温差: 0.5°C)

场景2:切换到DS18B20传感器 + LCD显示器
[控制器] 更换传感器: DS18B20
[DS18B20] 初始化
[控制器] 更换显示器: LCD
[LCD] 初始化显示器

--- 更新 ---
[DS18B20] 读取温度: 25.75°C
[LCD] 当前温度: 25.75
[LCD] 目标温度: 26.00
[加热器] 关闭
[风扇] 关闭
[控制器] 温度正常

[控制器] 停止温度控制
[加热器] 关闭
[风扇] 关闭


第四部分:提高可测试性

4.1 为什么接口提高可测试性

面向接口编程的一个重要优势是提高代码的可测试性。通过接口,我们可以:

  1. 创建模拟对象(Mock Objects):替换真实的硬件依赖
  2. 隔离测试:单独测试每个模块
  3. 自动化测试:无需硬件即可运行测试
  4. 测试边界条件:模拟各种异常情况

4.2 创建模拟对象

4.2.1 简单的模拟传感器

// mock_sensor.c - 模拟传感器用于测试
#include "interfaces.h"
#include <stdio.h>

// 模拟传感器的状态
static float mockTemperature = 25.0f;
static bool mockInitialized = false;
static int readCount = 0;

// 设置模拟温度(测试辅助函数)
void mockSensor_setTemperature(float temp) {
    mockTemperature = temp;
    printf("[Mock] 设置模拟温度: %.1f°C\n", temp);
}

// 获取读取次数(测试辅助函数)
int mockSensor_getReadCount(void) {
    return readCount;
}

// 重置模拟传感器(测试辅助函数)
void mockSensor_reset(void) {
    mockTemperature = 25.0f;
    mockInitialized = false;
    readCount = 0;
    printf("[Mock] 重置模拟传感器\n");
}

// 模拟传感器接口实现
static bool mock_init(void) {
    mockInitialized = true;
    printf("[Mock] 初始化模拟传感器\n");
    return true;
}

static float mock_read(void) {
    if (!mockInitialized) {
        printf("[Mock] 错误:传感器未初始化\n");
        return 0.0f;
    }

    readCount++;
    printf("[Mock] 读取模拟温度: %.1f°C (第%d次读取)\n", mockTemperature, readCount);
    return mockTemperature;
}

SensorInterface mockSensor = {
    .init = mock_init,
    .read = mock_read,
    .name = "MockSensor"
};

4.2.2 使用模拟对象进行测试

// test_temperature_controller.c
#include "temperature_controller.h"
#include "interfaces.h"
#include <stdio.h>
#include <assert.h>

// 外部模拟对象
extern SensorInterface mockSensor;
extern DisplayInterface mockDisplay;
extern ActuatorInterface mockHeater;
extern ActuatorInterface mockCooler;

// 测试辅助函数
extern void mockSensor_setTemperature(float temp);
extern int mockSensor_getReadCount(void);
extern void mockSensor_reset(void);
extern bool mockHeater_isOn(void);
extern bool mockCooler_isOn(void);

// 测试用例1:测试初始化
void test_controller_init(void) {
    printf("\n=== 测试用例1:控制器初始化 ===\n");

    TemperatureController controller;
    controller_init(&controller,
                   &mockSensor,
                   &mockDisplay,
                   &mockHeater,
                   &mockCooler,
                   26.0f,
                   0.5f);

    // 验证初始化
    assert(controller.tempSensor == &mockSensor);
    assert(controller.display == &mockDisplay);
    assert(controller.targetTemp == 26.0f);
    assert(controller.hysteresis == 0.5f);

    printf("✓ 初始化测试通过\n");
}

// 测试用例2:测试加热逻辑
void test_heating_logic(void) {
    printf("\n=== 测试用例2:加热逻辑 ===\n");

    mockSensor_reset();
    mockSensor_setTemperature(24.0f);  // 低于目标温度

    TemperatureController controller;
    controller_init(&controller, &mockSensor, &mockDisplay,
                   &mockHeater, &mockCooler, 26.0f, 0.5f);

    controller_start(&controller);
    controller_update(&controller);

    // 验证加热器应该开启
    assert(mockHeater_isOn() == true);
    assert(mockCooler_isOn() == false);

    printf("✓ 加热逻辑测试通过\n");
}

// 测试用例3:测试制冷逻辑
void test_cooling_logic(void) {
    printf("\n=== 测试用例3:制冷逻辑 ===\n");

    mockSensor_reset();
    mockSensor_setTemperature(28.0f);  // 高于目标温度

    TemperatureController controller;
    controller_init(&controller, &mockSensor, &mockDisplay,
                   &mockHeater, &mockCooler, 26.0f, 0.5f);

    controller_start(&controller);
    controller_update(&controller);

    // 验证风扇应该开启
    assert(mockHeater_isOn() == false);
    assert(mockCooler_isOn() == true);

    printf("✓ 制冷逻辑测试通过\n");
}

// 测试用例4:测试温度滞后
void test_hysteresis(void) {
    printf("\n=== 测试用例4:温度滞后 ===\n");

    mockSensor_reset();
    mockSensor_setTemperature(25.8f);  // 在滞后范围内

    TemperatureController controller;
    controller_init(&controller, &mockSensor, &mockDisplay,
                   &mockHeater, &mockCooler, 26.0f, 0.5f);

    controller_start(&controller);
    controller_update(&controller);

    // 验证执行器应该关闭(温差在滞后范围内)
    assert(mockHeater_isOn() == false);
    assert(mockCooler_isOn() == false);

    printf("✓ 温度滞后测试通过\n");
}

// 测试用例5:测试传感器切换
void test_sensor_switching(void) {
    printf("\n=== 测试用例5:传感器切换 ===\n");

    mockSensor_reset();

    TemperatureController controller;
    controller_init(&controller, &mockSensor, &mockDisplay,
                   &mockHeater, &mockCooler, 26.0f, 0.5f);

    // 第一次读取
    controller_start(&controller);
    controller_update(&controller);
    assert(mockSensor_getReadCount() == 1);

    // 切换传感器(实际上还是同一个mock,但测试切换逻辑)
    controller_setSensor(&controller, &mockSensor);

    // 再次读取
    controller_update(&controller);
    assert(mockSensor_getReadCount() == 2);

    printf("✓ 传感器切换测试通过\n");
}

// 运行所有测试
int main(void) {
    printf("=== 温度控制器单元测试 ===\n");

    test_controller_init();
    test_heating_logic();
    test_cooling_logic();
    test_hysteresis();
    test_sensor_switching();

    printf("\n=== 所有测试通过 ===\n");
    return 0;
}

4.3 测试驱动开发(TDD)

使用接口可以更好地实践测试驱动开发:

TDD流程: 1. 编写测试:先写测试用例,定义期望的行为 2. 实现接口:实现满足测试的最小功能 3. 重构代码:优化实现,保持测试通过 4. 重复循环:逐步完善功能

示例

// 步骤1:编写测试(测试先行)
void test_sensor_read_returns_valid_temperature(void) {
    SensorInterface* sensor = createMockSensor();

    float temp = sensor->read();

    assert(temp >= -40.0f && temp <= 125.0f);  // 合理的温度范围
}

// 步骤2:实现接口(最小实现)
static float mock_read(void) {
    return 25.0f;  // 简单返回固定值
}

// 步骤3:重构(优化实现)
static float mock_read(void) {
    // 添加更真实的模拟逻辑
    return mockTemperature + (rand() % 10) / 10.0f;  // 添加小幅波动
}


第五部分:实战项目

5.1 项目需求

设计一个多传感器数据采集系统,要求:

  1. 支持多种传感器:温度、湿度、光照、气压
  2. 支持多种显示方式:OLED、LCD、串口
  3. 支持多种存储方式:SD卡、Flash、云端
  4. 可配置的采集策略:定时采集、事件触发、阈值触发
  5. 易于扩展:添加新传感器或显示器不需要修改核心代码

5.2 系统架构设计

graph TB
    subgraph "应用层"
        A[数据采集器]
    end

    subgraph "接口层"
        B[传感器接口]
        C[显示接口]
        D[存储接口]
        E[策略接口]
    end

    subgraph "实现层"
        F1[DHT11]
        F2[BMP280]
        F3[BH1750]
        G1[OLED]
        G2[LCD]
        G3[UART]
        H1[SD卡]
        H2[Flash]
        H3[云存储]
        I1[定时策略]
        I2[事件策略]
    end

    A --> B
    A --> C
    A --> D
    A --> E

    B --> F1
    B --> F2
    B --> F3
    C --> G1
    C --> G2
    C --> G3
    D --> H1
    D --> H2
    D --> H3
    E --> I1
    E --> I2

5.3 接口定义

// data_acquisition_interfaces.h
#ifndef DATA_ACQUISITION_INTERFACES_H
#define DATA_ACQUISITION_INTERFACES_H

#include <stdint.h>
#include <stdbool.h>

// 传感器数据类型
typedef enum {
    SENSOR_TYPE_TEMPERATURE,
    SENSOR_TYPE_HUMIDITY,
    SENSOR_TYPE_PRESSURE,
    SENSOR_TYPE_LIGHT
} SensorType;

// 传感器数据结构
typedef struct {
    SensorType type;
    float value;
    uint32_t timestamp;
    const char* unit;
} SensorData;

// 通用传感器接口
typedef struct {
    bool (*init)(void);
    bool (*read)(SensorData* data);
    SensorType (*getType)(void);
    const char* name;
} GenericSensorInterface;

// 显示接口
typedef struct {
    bool (*init)(void);
    void (*clear)(void);
    void (*showData)(const SensorData* data);
    void (*showMultiData)(const SensorData* data, int count);
    const char* name;
} DisplayInterface;

// 存储接口
typedef struct {
    bool (*init)(void);
    bool (*save)(const SensorData* data);
    bool (*saveBatch)(const SensorData* data, int count);
    bool (*load)(SensorData* data, int* count);
    const char* name;
} StorageInterface;

// 采集策略接口
typedef struct {
    bool (*shouldCollect)(uint32_t currentTime);
    uint32_t (*getInterval)(void);
    void (*reset)(void);
    const char* name;
} CollectionStrategy;

#endif // DATA_ACQUISITION_INTERFACES_H

5.4 核心数据采集器实现

// data_collector.h
#ifndef DATA_COLLECTOR_H
#define DATA_COLLECTOR_H

#include "data_acquisition_interfaces.h"

#define MAX_SENSORS 10
#define MAX_DISPLAYS 3
#define MAX_STORAGES 3

typedef struct {
    // 依赖注入的组件
    GenericSensorInterface* sensors[MAX_SENSORS];
    int sensorCount;

    DisplayInterface* displays[MAX_DISPLAYS];
    int displayCount;

    StorageInterface* storages[MAX_STORAGES];
    int storageCount;

    CollectionStrategy* strategy;

    // 状态
    bool isRunning;
    uint32_t lastCollectionTime;
    SensorData dataBuffer[MAX_SENSORS];
    int bufferCount;
} DataCollector;

// 初始化
void collector_init(DataCollector* collector);

// 添加组件(依赖注入)
void collector_addSensor(DataCollector* collector, GenericSensorInterface* sensor);
void collector_addDisplay(DataCollector* collector, DisplayInterface* display);
void collector_addStorage(DataCollector* collector, StorageInterface* storage);
void collector_setStrategy(DataCollector* collector, CollectionStrategy* strategy);

// 控制
void collector_start(DataCollector* collector);
void collector_stop(DataCollector* collector);
void collector_update(DataCollector* collector, uint32_t currentTime);

// 手动采集
void collector_collectNow(DataCollector* collector);

#endif // DATA_COLLECTOR_H
// data_collector.c
#include "data_collector.h"
#include <stdio.h>
#include <string.h>

void collector_init(DataCollector* collector) {
    memset(collector, 0, sizeof(DataCollector));
    collector->isRunning = false;
    printf("[采集器] 初始化完成\n");
}

void collector_addSensor(DataCollector* collector, GenericSensorInterface* sensor) {
    if (collector->sensorCount < MAX_SENSORS) {
        collector->sensors[collector->sensorCount++] = sensor;
        printf("[采集器] 添加传感器: %s\n", sensor->name);

        if (sensor->init) {
            sensor->init();
        }
    }
}

void collector_addDisplay(DataCollector* collector, DisplayInterface* display) {
    if (collector->displayCount < MAX_DISPLAYS) {
        collector->displays[collector->displayCount++] = display;
        printf("[采集器] 添加显示器: %s\n", display->name);

        if (display->init) {
            display->init();
        }
    }
}

void collector_addStorage(DataCollector* collector, StorageInterface* storage) {
    if (collector->storageCount < MAX_STORAGES) {
        collector->storages[collector->storageCount++] = storage;
        printf("[采集器] 添加存储: %s\n", storage->name);

        if (storage->init) {
            storage->init();
        }
    }
}

void collector_setStrategy(DataCollector* collector, CollectionStrategy* strategy) {
    collector->strategy = strategy;
    printf("[采集器] 设置采集策略: %s\n", strategy->name);
}

void collector_start(DataCollector* collector) {
    collector->isRunning = true;
    collector->lastCollectionTime = 0;
    printf("[采集器] 启动数据采集\n");
}

void collector_stop(DataCollector* collector) {
    collector->isRunning = false;
    printf("[采集器] 停止数据采集\n");
}

void collector_collectNow(DataCollector* collector) {
    printf("\n[采集器] 开始采集数据...\n");

    collector->bufferCount = 0;

    // 从所有传感器读取数据
    for (int i = 0; i < collector->sensorCount; i++) {
        GenericSensorInterface* sensor = collector->sensors[i];
        if (sensor && sensor->read) {
            SensorData data;
            if (sensor->read(&data)) {
                collector->dataBuffer[collector->bufferCount++] = data;
            }
        }
    }

    printf("[采集器] 采集到 %d 条数据\n", collector->bufferCount);

    // 显示数据
    for (int i = 0; i < collector->displayCount; i++) {
        DisplayInterface* display = collector->displays[i];
        if (display && display->showMultiData) {
            display->showMultiData(collector->dataBuffer, collector->bufferCount);
        }
    }

    // 存储数据
    for (int i = 0; i < collector->storageCount; i++) {
        StorageInterface* storage = collector->storages[i];
        if (storage && storage->saveBatch) {
            storage->saveBatch(collector->dataBuffer, collector->bufferCount);
        }
    }
}

void collector_update(DataCollector* collector, uint32_t currentTime) {
    if (!collector->isRunning || !collector->strategy) {
        return;
    }

    // 根据策略判断是否需要采集
    if (collector->strategy->shouldCollect(currentTime)) {
        collector->lastCollectionTime = currentTime;
        collector_collectNow(collector);
    }
}

5.5 具体实现示例

// 定时采集策略
typedef struct {
    CollectionStrategy base;
    uint32_t interval;  // 采集间隔(毫秒)
    uint32_t lastTime;
} TimedStrategy;

static bool timed_shouldCollect(uint32_t currentTime) {
    TimedStrategy* strategy = (TimedStrategy*)/* 获取当前策略 */;
    if (currentTime - strategy->lastTime >= strategy->interval) {
        strategy->lastTime = currentTime;
        return true;
    }
    return false;
}

static uint32_t timed_getInterval(void) {
    TimedStrategy* strategy = (TimedStrategy*)/* 获取当前策略 */;
    return strategy->interval;
}

static void timed_reset(void) {
    TimedStrategy* strategy = (TimedStrategy*)/* 获取当前策略 */;
    strategy->lastTime = 0;
}

// 创建定时策略
CollectionStrategy* createTimedStrategy(uint32_t interval) {
    static TimedStrategy strategy;
    strategy.base.shouldCollect = timed_shouldCollect;
    strategy.base.getInterval = timed_getInterval;
    strategy.base.reset = timed_reset;
    strategy.base.name = "定时采集";
    strategy.interval = interval;
    strategy.lastTime = 0;
    return (CollectionStrategy*)&strategy;
}

5.6 主程序示例

// main.c
#include "data_collector.h"
#include <stdio.h>

// 外部对象声明
extern GenericSensorInterface dht11TempSensor;
extern GenericSensorInterface dht11HumiSensor;
extern GenericSensorInterface bmp280Sensor;
extern DisplayInterface oledDisplay;
extern DisplayInterface uartDisplay;
extern StorageInterface sdCardStorage;
extern StorageInterface flashStorage;

int main(void) {
    printf("=== 多传感器数据采集系统 ===\n\n");

    // 创建数据采集器
    DataCollector collector;
    collector_init(&collector);

    // 注入传感器
    collector_addSensor(&collector, &dht11TempSensor);
    collector_addSensor(&collector, &dht11HumiSensor);
    collector_addSensor(&collector, &bmp280Sensor);

    // 注入显示器
    collector_addDisplay(&collector, &oledDisplay);
    collector_addDisplay(&collector, &uartDisplay);

    // 注入存储
    collector_addStorage(&collector, &sdCardStorage);
    collector_addStorage(&collector, &flashStorage);

    // 设置采集策略
    CollectionStrategy* strategy = createTimedStrategy(5000);  // 每5秒采集一次
    collector_setStrategy(&collector, strategy);

    // 启动采集
    collector_start(&collector);

    // 模拟运行
    uint32_t currentTime = 0;
    for (int i = 0; i < 3; i++) {
        currentTime += 5000;  // 模拟时间流逝
        printf("\n=== 时间: %d ms ===\n", currentTime);
        collector_update(&collector, currentTime);
    }

    // 停止采集
    collector_stop(&collector);

    return 0;
}

验证方法

验证步骤

  1. 编译代码

    # 使用GCC编译
    gcc -o interface_demo main.c sensor_impl.c display_impl.c \
        temperature_controller.c -I./include
    
    # 或使用Makefile
    make all
    

  2. 运行程序

    ./interface_demo
    

  3. 观察输出

  4. 检查各组件是否正确初始化
  5. 验证依赖注入是否生效
  6. 确认运行时切换组件是否正常

  7. 运行测试

    # 编译测试程序
    gcc -o test_controller test_temperature_controller.c \
        temperature_controller.c mock_sensor.c -I./include
    
    # 运行测试
    ./test_controller
    

预期结果

  • 所有组件正确初始化
  • 依赖注入成功,组件可以互相替换
  • 测试用例全部通过
  • 系统运行稳定,无内存泄漏

故障排除

常见问题

问题1:函数指针为NULL导致程序崩溃

原因:接口对象未正确初始化,或者某些函数指针未赋值

解决方案

// 在调用前检查函数指针
if (sensor && sensor->read) {
    float temp = sensor->read();
} else {
    printf("错误:传感器接口未初始化\n");
}

问题2:依赖注入后对象行为异常

原因:注入的对象生命周期管理不当,可能是局部变量被销毁

解决方案

// 使用静态变量或全局变量
static SensorInterface dht11Sensor = {
    .init = dht11_init,
    .read = dht11_read,
    .name = "DHT11"
};

// 或使用动态分配
SensorInterface* sensor = malloc(sizeof(SensorInterface));

问题3:接口切换后状态不一致

原因:切换接口时未重新初始化

解决方案

void controller_setSensor(TemperatureController* ctrl, SensorInterface* sensor) {
    ctrl->sensor = sensor;
    // 重新初始化新传感器
    if (sensor && sensor->init) {
        sensor->init();
    }
}


总结

关键要点

  1. 面向接口编程的核心
  2. 依赖抽象而不是具体实现
  3. 通过接口定义契约
  4. 实现与接口分离

  5. C语言中的接口实现

  6. 使用函数指针模拟接口
  7. 使用结构体封装接口方法
  8. 通过依赖注入实现解耦

  9. 依赖注入的优势

  10. 提高代码灵活性
  11. 便于单元测试
  12. 支持运行时替换组件

  13. 接口设计原则

  14. 接口应该小而专注
  15. 遵循单一职责原则
  16. 提供清晰的错误处理

最佳实践

  1. 接口设计
  2. 定义清晰的接口契约
  3. 使用有意义的命名
  4. 包含必要的元数据

  5. 依赖管理

  6. 优先使用构造函数注入
  7. 检查依赖的有效性
  8. 管理好对象生命周期

  9. 测试策略

  10. 创建模拟对象进行测试
  11. 隔离测试每个模块
  12. 实践测试驱动开发

  13. 代码组织

  14. 接口定义独立文件
  15. 实现与接口分离
  16. 保持清晰的目录结构

下一步学习


延伸阅读

推荐资源

书籍: - 《设计模式:可复用面向对象软件的基础》- Gang of Four - 《重构:改善既有代码的设计》- Martin Fowler - 《Clean Architecture》- Robert C. Martin

在线资源: - SOLID原则详解 - 依赖注入模式 - 接口隔离原则

相关标准: - MISRA C编码规范 - AUTOSAR架构标准 - ISO 26262功能安全标准

实践项目建议

  1. 重构现有项目
  2. 识别紧耦合的代码
  3. 提取接口抽象
  4. 应用依赖注入

  5. 设计新系统

  6. 从接口设计开始
  7. 定义清晰的模块边界
  8. 使用依赖注入组装系统

  9. 编写测试

  10. 为现有代码添加测试
  11. 使用模拟对象隔离依赖
  12. 提高测试覆盖率

文档版本: 1.0
最后更新: 2026-03-10
作者: 嵌入式知识平台
许可: CC BY-SA 4.0