前言

“C 是面向过程的语言,不能面向对象” —— 这是对 C 最大的误解。

Linux 内核、GLib、CPython、Redis 全部用 C 写成,无一不是面向对象设计的典范。C 没有 class 关键字,但拥有实现 OOP 的全部底层能力:结构体 = 数据封装,函数指针 = 多态,结构体嵌套 = 继承

本文将系统展示:如何用纯 C 实现面向对象三大特性 + SOLID 五大原则 + 6 种经典设计模式。

📖 参考:Linux 内核设备模型、GObject 类型系统、RT-Thread 设备框架


目录

  1. SOLID 原则在 C 中的映射
  2. 三大基石:封装、继承、多态
  3. 设计模式实战
  4. 综合实例:一个完整的插件框架
  5. 常见问题与最佳实践

一、SOLID 原则在 C 中的映射

SOLID 原则含义C 语言实现手段
S — 单一职责一个模块只有一个变化原因一个 .c 文件 = 一个模块,只管一件事
O — 开闭原则对扩展开放,对修改关闭函数指针表(新增实现不改已有代码)
L — 里氏替换子类可替换父类所有"子类"结构体首成员嵌入"父类"
I — 接口隔离不强迫依赖不需要的接口拆分多个小的 ops 结构体
D — 依赖倒置高层不依赖低层,都依赖抽象通过接口(函数指针)交互,不直接调用

下面逐一展开,每条原则都有可编译的代码示例。


二、三大基石:封装、继承、多态

2.1 封装(Encapsulation)

核心思想: 隐藏内部实现,只暴露操作接口。

手法一:不透明指针(Opaque Pointer)
/* ========== stack.h — 外界只知道"存在这个类型" ========== */
#pragma once
#include <stdbool.h>

typedef struct stack stack_t;    /* 前向声明,内部不可见 */

stack_t *stack_create(int capacity);
void     stack_destroy(stack_t *s);
bool     stack_push(stack_t *s, void *item);
void    *stack_pop(stack_t *s);
void    *stack_peek(const stack_t *s);
int      stack_size(const stack_t *s);
bool     stack_is_empty(const stack_t *s);
/* ========== stack.c — 结构体定义只存在于此 ========== */
#include "stack.h"
#include <stdlib.h>

struct stack {
    void **items;       /* 外部完全无法访问这些字段 */
    int    capacity;
    int    top;
};

stack_t *stack_create(int capacity) {
    stack_t *s = malloc(sizeof(stack_t));
    if (!s) return NULL;
    s->items = malloc(sizeof(void *) * capacity);
    s->capacity = capacity;
    s->top = -1;
    return s;
}

bool stack_push(stack_t *s, void *item) {
    if (s->top >= s->capacity - 1) return false;
    s->items[++s->top] = item;
    return true;
}

/* ... 其余实现 ... */

💡 效果: 外部代码不可能写出 s->top = 999 这种越权操作。修改内部数据结构(如改为链表实现),不需要重编任何引用方。

手法二:static 隐藏模块私有函数
/* utils.c */

/* static 函数 = private 方法,文件外不可见 */
static int calculate_hash(const char *key) {
    /* ... 内部算法 ... */
}

static void resize_table(hashtable_t *ht) {
    /* ... 内部操作 ... */
}

/* 非 static = public 方法 */
int hashtable_insert(hashtable_t *ht, const char *key, void *value) {
    int hash = calculate_hash(key);   /* 内部调用 */
    /* ... */
}

2.2 继承(Inheritance)

C 的继承手法:结构体首成员嵌入。

原理:C 标准保证结构体首成员地址 = 结构体本身地址。因此"父类"指针和"子类"指针可以互相安全转换。

/* ========== 定义"基类" Shape ========== */
typedef struct shape shape_t;

typedef struct {
    double (*area)(const shape_t *self);
    double (*perimeter)(const shape_t *self);
    void   (*draw)(const shape_t *self);
    void   (*destroy)(shape_t *self);
} shape_ops_t;

struct shape {
    const shape_ops_t *ops;     /* 虚函数表 */
    const char        *name;
    int                x, y;    /* 公共属性 */
};

/* "基类"方法:通过 ops 转发 */
static inline double shape_area(const shape_t *s) {
    return s->ops->area(s);
}
static inline double shape_perimeter(const shape_t *s) {
    return s->ops->perimeter(s);
}
static inline void shape_draw(const shape_t *s) {
    s->ops->draw(s);
}
/* ========== "子类" Circle ========== */
typedef struct {
    shape_t base;       /* ← 首成员是基类,实现"继承" */
    double  radius;     /* 子类特有属性 */
} circle_t;

/* 子类方法实现 */
static double circle_area(const shape_t *self) {
    const circle_t *c = (const circle_t *)self;   /* 安全向下转型 */
    return 3.14159265 * c->radius * c->radius;
}

static double circle_perimeter(const shape_t *self) {
    const circle_t *c = (const circle_t *)self;
    return 2.0 * 3.14159265 * c->radius;
}

static void circle_draw(const shape_t *self) {
    const circle_t *c = (const circle_t *)self;
    printf("Drawing circle at (%d,%d) r=%.1f\n",
           self->x, self->y, c->radius);
}

static void circle_destroy(shape_t *self) {
    free(self);
}

/* 子类的虚函数表(静态常量,全局只一份)*/
static const shape_ops_t circle_ops = {
    .area      = circle_area,
    .perimeter = circle_perimeter,
    .draw      = circle_draw,
    .destroy   = circle_destroy,
};

/* 子类"构造函数" */
shape_t *circle_create(int x, int y, double radius) {
    circle_t *c = malloc(sizeof(circle_t));
    c->base.ops  = &circle_ops;
    c->base.name = "Circle";
    c->base.x    = x;
    c->base.y    = y;
    c->radius    = radius;
    return (shape_t *)c;    /* 返回基类指针 */
}
/* ========== "子类" Rectangle ========== */
typedef struct {
    shape_t base;       /* 继承 */
    double  width;
    double  height;
} rectangle_t;

static double rect_area(const shape_t *self) {
    const rectangle_t *r = (const rectangle_t *)self;
    return r->width * r->height;
}

static double rect_perimeter(const shape_t *self) {
    const rectangle_t *r = (const rectangle_t *)self;
    return 2.0 * (r->width + r->height);
}

static void rect_draw(const shape_t *self) {
    const rectangle_t *r = (const rectangle_t *)self;
    printf("Drawing rect at (%d,%d) w=%.1f h=%.1f\n",
           self->x, self->y, r->width, r->height);
}

static const shape_ops_t rect_ops = {
    .area      = rect_area,
    .perimeter = rect_perimeter,
    .draw      = rect_draw,
    .destroy   = circle_destroy,    /* 析构逻辑相同可复用 */
};

shape_t *rectangle_create(int x, int y, double w, double h) {
    rectangle_t *r = malloc(sizeof(rectangle_t));
    r->base.ops  = &rect_ops;
    r->base.name = "Rectangle";
    r->base.x    = x;
    r->base.y    = y;
    r->width     = w;
    r->height    = h;
    return (shape_t *)r;
}

2.3 多态(Polymorphism)

有了上面的基础,多态就是自然而然的事:

int main(void) {
    /* 创建不同"子类"对象,用统一的"基类"指针管理 */
    shape_t *shapes[] = {
        circle_create(0, 0, 5.0),
        rectangle_create(1, 2, 4.0, 6.0),
        circle_create(3, 3, 2.5),
    };
    int n = sizeof(shapes) / sizeof(shapes[0]);

    /* 多态调用:同一个接口,不同行为 */
    for (int i = 0; i < n; i++) {
        printf("[%s] area=%.2f perimeter=%.2f\n",
               shapes[i]->name,
               shape_area(shapes[i]),
               shape_perimeter(shapes[i]));
        shape_draw(shapes[i]);
    }

    /* 统一销毁 */
    for (int i = 0; i < n; i++) {
        shapes[i]->ops->destroy(shapes[i]);
    }
    return 0;
}

输出:

[Circle] area=78.54 perimeter=31.42
Drawing circle at (0,0) r=5.0
[Rectangle] area=24.00 perimeter=20.00
Drawing rect at (1,2) w=4.0 h=6.0
[Circle] area=19.63 perimeter=15.71
Drawing circle at (3,3) r=2.5

💡 这就是 C 的多态。 调用方只认识 shape_t *,不需要 #include 任何子类头文件。新增一个 triangle_t 不改已有任何代码——满足开闭原则(O)


2.4 三大特性总结

┌─────────────────────────────────────────────────────────┐
│                    C 的面向对象模型                        │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  封装 = opaque pointer + static 函数                     │
│  继承 = 结构体首成员嵌入                                   │
│  多态 = 函数指针表(vtable)                              │
│                                                         │
│  构造 = xxx_create() 函数                                │
│  析构 = xxx_destroy() 函数                               │
│  this = 第一个参数 self                                   │
│                                                         │
└─────────────────────────────────────────────────────────┘

三、设计模式实战

3.1 策略模式(Strategy)— 满足 O(开闭)+ D(依赖倒置)

场景: 排序算法可切换,但调用方不关心具体用的是哪种。

/* ===== 定义策略接口 ===== */
typedef int (*compare_fn)(const void *a, const void *b);

typedef struct {
    const char *name;
    void (*sort)(void *arr, int n, int elem_size, compare_fn cmp);
} sort_strategy_t;

/* ===== 具体策略实现 ===== */
static void bubble_sort_impl(void *arr, int n, int elem_size, compare_fn cmp) {
    uint8_t *base = (uint8_t *)arr;
    uint8_t *tmp = alloca(elem_size);
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - 1 - i; j++) {
            void *a = base + j * elem_size;
            void *b = base + (j + 1) * elem_size;
            if (cmp(a, b) > 0) {
                memcpy(tmp, a, elem_size);
                memcpy(a, b, elem_size);
                memcpy(b, tmp, elem_size);
            }
        }
    }
}

static void quick_sort_impl(void *arr, int n, int elem_size, compare_fn cmp) {
    qsort(arr, n, elem_size, cmp);  /* 借用标准库 */
}

/* 策略实例(全局常量)*/
const sort_strategy_t SORT_BUBBLE = { .name = "bubble", .sort = bubble_sort_impl };
const sort_strategy_t SORT_QUICK  = { .name = "quick",  .sort = quick_sort_impl  };

/* ===== 上下文(Context)===== */
typedef struct {
    const sort_strategy_t *strategy;    /* 当前策略 */
} sorter_t;

void sorter_set_strategy(sorter_t *s, const sort_strategy_t *strategy) {
    s->strategy = strategy;
}

void sorter_execute(sorter_t *s, void *arr, int n, int elem_size, compare_fn cmp) {
    printf("Using [%s] sort\n", s->strategy->name);
    s->strategy->sort(arr, n, elem_size, cmp);
}

/* ===== 使用 ===== */
int int_compare(const void *a, const void *b) {
    return *(int *)a - *(int *)b;
}

int main(void) {
    int data[] = {5, 2, 8, 1, 9, 3};
    sorter_t sorter = { .strategy = &SORT_QUICK };

    sorter_execute(&sorter, data, 6, sizeof(int), int_compare);

    /* 运行时切换策略 —— 不改任何已有代码 */
    sorter_set_strategy(&sorter, &SORT_BUBBLE);
    sorter_execute(&sorter, data, 6, sizeof(int), int_compare);
}

满足的 SOLID 原则:

  • O(开闭): 新增排序算法只需新定义一个 sort_strategy_t 实例
  • D(依赖倒置): sorter_t 依赖抽象接口(sort_strategy_t),不依赖具体实现
  • S(单一职责): 每个策略只负责一种排序算法

3.2 观察者模式(Observer)— 满足 O + D

场景: 事件发生时通知所有订阅者,发布者不知道谁在订阅。

/* ===== 事件系统 ===== */
#define MAX_OBSERVERS 16

typedef void (*event_handler_t)(void *sender, void *event_data);

typedef struct {
    event_handler_t handlers[MAX_OBSERVERS];
    int count;
} event_t;

void event_init(event_t *evt) {
    evt->count = 0;
}

int event_subscribe(event_t *evt, event_handler_t handler) {
    if (evt->count >= MAX_OBSERVERS) return -1;
    evt->handlers[evt->count++] = handler;
    return 0;
}

void event_unsubscribe(event_t *evt, event_handler_t handler) {
    for (int i = 0; i < evt->count; i++) {
        if (evt->handlers[i] == handler) {
            evt->handlers[i] = evt->handlers[--evt->count];
            return;
        }
    }
}

void event_publish(event_t *evt, void *sender, void *data) {
    for (int i = 0; i < evt->count; i++) {
        evt->handlers[i](sender, data);
    }
}

/* ===== 使用示例 ===== */
typedef struct {
    float temperature;
    float humidity;
} sensor_data_t;

/* 发布者 */
typedef struct {
    event_t on_data_ready;     /* 事件:数据就绪 */
    event_t on_alarm;          /* 事件:报警 */
} sensor_t;

void sensor_init(sensor_t *s) {
    event_init(&s->on_data_ready);
    event_init(&s->on_alarm);
}

void sensor_update(sensor_t *s, float temp, float humi) {
    sensor_data_t data = { .temperature = temp, .humidity = humi };
    event_publish(&s->on_data_ready, s, &data);
    if (temp > 80.0f) {
        event_publish(&s->on_alarm, s, &data);
    }
}

/* 订阅者们 —— 彼此独立,互不知晓 */
void display_handler(void *sender, void *event_data) {
    sensor_data_t *d = (sensor_data_t *)event_data;
    printf("[Display] Temp=%.1f Humi=%.1f\n", d->temperature, d->humidity);
}

void logger_handler(void *sender, void *event_data) {
    sensor_data_t *d = (sensor_data_t *)event_data;
    printf("[Logger] Recording: T=%.1f H=%.1f\n", d->temperature, d->humidity);
}

void alarm_handler(void *sender, void *event_data) {
    printf("[ALARM] Temperature too high!\n");
}

int main(void) {
    sensor_t sensor;
    sensor_init(&sensor);

    /* 订阅 —— 发布者不知道谁订阅了 */
    event_subscribe(&sensor.on_data_ready, display_handler);
    event_subscribe(&sensor.on_data_ready, logger_handler);
    event_subscribe(&sensor.on_alarm, alarm_handler);

    /* 正常数据 */
    sensor_update(&sensor, 25.0f, 60.0f);
    /* 高温触发报警 */
    sensor_update(&sensor, 85.0f, 40.0f);
}

输出:

[Display] Temp=25.0 Humi=60.0
[Logger] Recording: T=25.0 H=60.0
[Display] Temp=85.0 Humi=40.0
[Logger] Recording: T=85.0 H=40.0
[ALARM] Temperature too high!

3.3 状态模式(State)— 满足 S + O

场景: 对象行为随状态变化,避免巨型 switch-case。

/* ===== 状态接口 ===== */
typedef struct player player_t;

typedef struct {
    const char *name;
    void (*play)(player_t *p);
    void (*pause)(player_t *p);
    void (*stop)(player_t *p);
} player_state_t;

/* ===== 播放器上下文 ===== */
struct player {
    const player_state_t *state;
    char current_track[64];
};

/* ===== 状态前向声明 ===== */
extern const player_state_t STATE_STOPPED;
extern const player_state_t STATE_PLAYING;
extern const player_state_t STATE_PAUSED;

/* ===== Stopped 状态 ===== */
static void stopped_play(player_t *p) {
    printf("▶ Start playing: %s\n", p->current_track);
    p->state = &STATE_PLAYING;
}
static void stopped_pause(player_t *p) {
    printf("⚠ Already stopped, cannot pause\n");
}
static void stopped_stop(player_t *p) {
    printf("⚠ Already stopped\n");
}

const player_state_t STATE_STOPPED = {
    .name = "Stopped", .play = stopped_play,
    .pause = stopped_pause, .stop = stopped_stop,
};

/* ===== Playing 状态 ===== */
static void playing_play(player_t *p) {
    printf("⚠ Already playing\n");
}
static void playing_pause(player_t *p) {
    printf("⏸ Paused\n");
    p->state = &STATE_PAUSED;
}
static void playing_stop(player_t *p) {
    printf("⏹ Stopped\n");
    p->state = &STATE_STOPPED;
}

const player_state_t STATE_PLAYING = {
    .name = "Playing", .play = playing_play,
    .pause = playing_pause, .stop = playing_stop,
};

/* ===== Paused 状态 ===== */
static void paused_play(player_t *p) {
    printf("▶ Resumed playing\n");
    p->state = &STATE_PLAYING;
}
static void paused_pause(player_t *p) {
    printf("⚠ Already paused\n");
}
static void paused_stop(player_t *p) {
    printf("⏹ Stopped from pause\n");
    p->state = &STATE_STOPPED;
}

const player_state_t STATE_PAUSED = {
    .name = "Paused", .play = paused_play,
    .pause = paused_pause, .stop = paused_stop,
};

/* ===== 统一调用接口 ===== */
void player_play(player_t *p)  { p->state->play(p); }
void player_pause(player_t *p) { p->state->pause(p); }
void player_stop(player_t *p)  { p->state->stop(p); }

/* ===== 使用 ===== */
int main(void) {
    player_t player = {
        .state = &STATE_STOPPED,
        .current_track = "Yesterday Once More",
    };

    printf("State: %s\n", player.state->name);
    player_play(&player);    /* Stopped → Playing */
    player_pause(&player);   /* Playing → Paused */
    player_play(&player);    /* Paused → Playing */
    player_stop(&player);    /* Playing → Stopped */
    player_pause(&player);   /* Stopped: cannot pause */
}

💡 对比 switch-case: 新增一个状态(如 STATE_FAST_FORWARD),只需定义新的 player_state_t 实例,不修改任何已有状态的代码


3.4 工厂模式(Factory)— 满足 D(依赖倒置)

场景: 根据类型字符串/枚举创建不同对象,调用方不依赖具体类型。

/* ===== 产品接口(使用前面定义的 shape 体系)===== */

/* 工厂函数:根据名字创建对象 */
typedef shape_t *(*shape_creator_t)(int x, int y, const char *params);

typedef struct {
    const char      *type_name;
    shape_creator_t  create;
} shape_factory_entry_t;

/* ===== 工厂注册表 ===== */
#define MAX_FACTORY_ENTRIES 16

static shape_factory_entry_t s_registry[MAX_FACTORY_ENTRIES];
static int s_registry_count = 0;

void shape_factory_register(const char *type_name, shape_creator_t creator) {
    if (s_registry_count >= MAX_FACTORY_ENTRIES) return;
    s_registry[s_registry_count].type_name = type_name;
    s_registry[s_registry_count].create = creator;
    s_registry_count++;
}

shape_t *shape_factory_create(const char *type_name, int x, int y, const char *params) {
    for (int i = 0; i < s_registry_count; i++) {
        if (strcmp(s_registry[i].type_name, type_name) == 0) {
            return s_registry[i].create(x, y, params);
        }
    }
    fprintf(stderr, "Unknown shape type: %s\n", type_name);
    return NULL;
}

/* ===== 各"子类"注册自己 ===== */
static shape_t *create_circle(int x, int y, const char *params) {
    double radius = atof(params);
    return circle_create(x, y, radius);
}

static shape_t *create_rect(int x, int y, const char *params) {
    double w, h;
    sscanf(params, "%lf,%lf", &w, &h);
    return rectangle_create(x, y, w, h);
}

void shapes_register_all(void) {
    shape_factory_register("circle", create_circle);
    shape_factory_register("rectangle", create_rect);
    /* 新增形状?只需在这里加一行注册 */
}

/* ===== 使用:调用方完全不依赖具体类型 ===== */
int main(void) {
    shapes_register_all();

    /* 可以从配置文件/网络协议中读取类型名 */
    shape_t *s1 = shape_factory_create("circle", 0, 0, "5.0");
    shape_t *s2 = shape_factory_create("rectangle", 1, 1, "4.0,6.0");

    shape_draw(s1);
    shape_draw(s2);

    s1->ops->destroy(s1);
    s2->ops->destroy(s2);
}

3.5 单例模式(Singleton)

/* 方法一:函数内 static(线程不安全,适合单线程/初始化阶段)*/
logger_t *logger_get_instance(void) {
    static logger_t instance;
    static bool initialized = false;
    
    if (!initialized) {
        instance.level = LOG_INFO;
        instance.output = stderr;
        initialized = true;
    }
    return &instance;
}

/* 方法二:pthread_once(线程安全)*/
#include <pthread.h>

static logger_t *s_instance = NULL;
static pthread_once_t s_once = PTHREAD_ONCE_INIT;

static void logger_init_once(void) {
    s_instance = malloc(sizeof(logger_t));
    s_instance->level = LOG_INFO;
    s_instance->output = stderr;
}

logger_t *logger_get_instance(void) {
    pthread_once(&s_once, logger_init_once);
    return s_instance;
}

3.6 装饰器模式(Decorator)— 满足 O

场景: 动态给 stream 添加加密、压缩、缓冲等功能,不修改原有 stream。

/* ===== 基础 stream 接口(同前面的定义)===== */
typedef struct stream stream_t;
typedef struct {
    int  (*read)(stream_t *self, void *buf, int size);
    int  (*write)(stream_t *self, const void *buf, int size);
    void (*close)(stream_t *self);
} stream_ops_t;

struct stream {
    const stream_ops_t *ops;
    void               *priv;
};

/* ===== 装饰器:加密 stream ===== */
typedef struct {
    stream_t  base;          /* "继承" stream */
    stream_t *wrapped;       /* 被装饰的原始 stream */
    uint8_t   xor_key;       /* 加密密钥 */
} encrypted_stream_t;

static int encrypted_write(stream_t *self, const void *buf, int size) {
    encrypted_stream_t *es = (encrypted_stream_t *)self;
    uint8_t *tmp = malloc(size);
    
    /* 加密处理 */
    for (int i = 0; i < size; i++) {
        tmp[i] = ((const uint8_t *)buf)[i] ^ es->xor_key;
    }
    
    /* 委托给被包装的 stream */
    int ret = es->wrapped->ops->write(es->wrapped, tmp, size);
    free(tmp);
    return ret;
}

static int encrypted_read(stream_t *self, void *buf, int size) {
    encrypted_stream_t *es = (encrypted_stream_t *)self;
    int ret = es->wrapped->ops->read(es->wrapped, buf, size);
    
    /* 解密 */
    for (int i = 0; i < ret; i++) {
        ((uint8_t *)buf)[i] ^= es->xor_key;
    }
    return ret;
}

static const stream_ops_t encrypted_ops = {
    .read  = encrypted_read,
    .write = encrypted_write,
    .close = /* ... */
};

/* 包装函数:给任意 stream 加上加密能力 */
stream_t *stream_add_encryption(stream_t *inner, uint8_t key) {
    encrypted_stream_t *es = malloc(sizeof(encrypted_stream_t));
    es->base.ops = &encrypted_ops;
    es->wrapped  = inner;
    es->xor_key  = key;
    return (stream_t *)es;
}

/* ===== 使用:可以层层嵌套装饰 ===== */
stream_t *s = file_stream_open("data.bin");
s = stream_add_encryption(s, 0xAB);     /* 加密装饰 */
s = stream_add_buffering(s, 4096);      /* 缓冲装饰(类似实现)*/

stream_read(s, buf, len);   /* 透明地先解缓冲再解密 */

四、综合实例:一个完整的插件框架

将以上模式融合,实现一个可注册、可扩展的命令处理框架

/* ===== 命令接口定义 ===== */
typedef struct {
    const char *name;
    const char *help;
    int (*execute)(int argc, char **argv);
} command_t;

/* ===== 命令注册表 ===== */
#define MAX_COMMANDS 64

static const command_t *s_commands[MAX_COMMANDS];
static int s_cmd_count = 0;

void command_register(const command_t *cmd) {
    if (s_cmd_count < MAX_COMMANDS) {
        s_commands[s_cmd_count++] = cmd;
    }
}

int command_execute(const char *name, int argc, char **argv) {
    for (int i = 0; i < s_cmd_count; i++) {
        if (strcmp(s_commands[i]->name, name) == 0) {
            return s_commands[i]->execute(argc, argv);
        }
    }
    printf("Unknown command: %s\n", name);
    return -1;
}

void command_list_all(void) {
    printf("Available commands:\n");
    for (int i = 0; i < s_cmd_count; i++) {
        printf("  %-12s %s\n", s_commands[i]->name, s_commands[i]->help);
    }
}

/* ===== 各模块定义自己的命令(分散注册)===== */

/* --- 文件模块 --- */
static int cmd_ls(int argc, char **argv) {
    printf("Listing files...\n");
    return 0;
}
static int cmd_cat(int argc, char **argv) {
    if (argc < 2) { printf("Usage: cat <file>\n"); return -1; }
    printf("Content of %s: ...\n", argv[1]);
    return 0;
}
static const command_t CMD_LS  = { "ls",  "List directory", cmd_ls };
static const command_t CMD_CAT = { "cat", "Print file",     cmd_cat };

/* --- 网络模块 --- */
static int cmd_ping(int argc, char **argv) {
    if (argc < 2) { printf("Usage: ping <host>\n"); return -1; }
    printf("Pinging %s...\n", argv[1]);
    return 0;
}
static const command_t CMD_PING = { "ping", "Ping host", cmd_ping };

/* --- 系统模块 --- */
static int cmd_help(int argc, char **argv) {
    command_list_all();
    return 0;
}
static const command_t CMD_HELP = { "help", "Show all commands", cmd_help };

/* ===== 各模块注册 ===== */
void file_module_init(void) {
    command_register(&CMD_LS);
    command_register(&CMD_CAT);
}

void net_module_init(void) {
    command_register(&CMD_PING);
}

void sys_module_init(void) {
    command_register(&CMD_HELP);
}

/* ===== 主程序 ===== */
int main(void) {
    /* 各模块独立注册,互不依赖 */
    file_module_init();
    net_module_init();
    sys_module_init();

    /* 执行命令 */
    char *args1[] = {"ls"};
    command_execute("ls", 1, args1);

    char *args2[] = {"ping", "google.com"};
    command_execute("ping", 2, args2);

    command_execute("help", 0, NULL);
}

SOLID 分析:

原则如何满足
S — 单一职责每个模块只管自己的命令
O — 开闭原则新增命令 = 新增模块 + 注册,不改框架代码
L — 里氏替换所有 command_t 都可被框架统一调用
I — 接口隔离command_t 接口极简,只有必要字段
D — 依赖倒置框架依赖 command_t 接口,不依赖具体模块

五、常见问题与最佳实践

5.1 模式对比速查表

模式核心结构解决什么问题C 实现手法
策略接口 + 多个实现算法可替换函数指针结构体
观察者事件 + 订阅列表一对多通知解耦回调数组
状态状态接口 + 状态切换消灭 switch-case每状态一个 ops
工厂注册表 + 创建函数创建与使用解耦函数指针注册
装饰器包装 + 委托动态叠加功能结构体嵌套 + 转发
单例静态实例 + 访问函数全局唯一对象static + once

5.2 何时用、何时不用

✅ 用面向对象的场景:
   • 同类对象有多种实现(驱动、协议、算法)
   • 需要运行时切换行为
   • 模块间需要松耦合
   • 系统需要可扩展性

❌ 不要过度设计的场景:
   • 只有一种实现且未来不会变化
   • 简单的数据处理流水线
   • 性能关键路径(虚函数调用有间接跳转开销)
   • 代码总量 < 500 行的小工具

5.3 性能考量

/* 函数指针调用 vs 直接调用 */

/* 直接调用:编译器可内联,分支预测友好 */
int result = concrete_function(data);

/* 间接调用:多一次内存读取 + 无法内联 */
int result = obj->ops->do_something(obj, data);

/* 
 * 开销:约 1~5ns(现代 CPU),绝大多数场景可忽略
 * 真正的性能瓶颈从来不在这里,而在算法和 I/O
 * 但在超高频热路径(每秒千万次),请 benchmark 验证
 */

5.4 类型安全强化技巧

/* 问题:void * 丢失了类型信息,容易误用 */

/* 技巧:用宏生成类型安全的容器 */
#define DEFINE_TYPED_LIST(name, type)                            \
    typedef struct {                                             \
        type   *items;                                          \
        int     count;                                          \
        int     capacity;                                       \
    } name##_list_t;                                            \
                                                                \
    static inline void name##_list_push(name##_list_t *l, type item) { \
        if (l->count >= l->capacity) { /* resize... */ }        \
        l->items[l->count++] = item;                            \
    }                                                           \
    static inline type name##_list_get(name##_list_t *l, int i) {     \
        return l->items[i];                                     \
    }

/* 使用 */
DEFINE_TYPED_LIST(int, int)
DEFINE_TYPED_LIST(point, point_t)

int_list_t numbers = {0};
int_list_push(&numbers, 42);       /* 类型安全 */
// int_list_push(&numbers, "hello"); /* 编译错误!*/

总结:C 语言 OOP 的核心公式

┌─────────────────────────────────────────────────────────────┐
│                                                             │
│  类         =  结构体 + 操作函数(第一参数为 self)           │
│  私有成员   =  定义在 .c 中(opaque pointer)                │
│  继承       =  子类结构体首成员 = 父类结构体                  │
│  多态       =  函数指针表(const ops 结构体)                 │
│  构造/析构  =  xxx_create() / xxx_destroy()                 │
│  接口       =  只含函数指针的结构体                           │
│  抽象类     =  ops 中有些函数指针允许为 NULL                  │
│                                                             │
│  SOLID 的核心实现手段 = 函数指针 + 注册机制 + 分层隔离        │
│                                                             │
└─────────────────────────────────────────────────────────────┘

记住:面向对象是一种思想,不是一种语法。 C 没有语法糖的辅助,反而让你更深刻地理解 OOP 的本质——封装是为了管理复杂度,多态是为了可扩展性,继承是为了代码复用

当你用 C 手动实现这些机制时,你对 OOP 的理解将超越 90% 只会用 class 关键字的人。


写 C 的面向对象,不是为了模仿 C++,而是为了让代码结构清晰、职责分明、易于扩展。这就是 Linux 内核几千万行代码依然可维护的秘密。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐