云计算百科
云计算领域专业知识百科平台

创建型模式:组合模式(C语言实现与嵌入式实战)

C语言开发者尤其是嵌入式方向,常面临需同时处理单个对象与同类对象集合的场景。如文件系统中操作文件(单个)与目录(集合),嵌入式GUI中处理按钮(单个)与布局容器(集合)。若为两者分别编写逻辑,会导致代码臃肿,维护扩展困难。

组合模式可精准解决此痛点,核心是“统一”——将单个与组合对象封装为同类结构,用相同接口处理,无需区分类型。本文从核心思想切入,手把手实现C语言版本,结合文件系统、嵌入式GUI实战落地,最后分享递归设计要点与性能优化技巧。

一、核心思想:用树形结构统一单个与组合对象

组合模式核心定义:将对象组合成树形结构,使客户端对单个对象和组合对象的使用具有一致性。通俗理解:单个对象为“叶子”,组合对象为“树枝”,树枝可嵌套叶子或更小树枝,无论操作叶子还是树枝,均采用同一方式(如显示、删除)。

组合模式有三个核心角色,是实现基础,C开发者可理解为:

  • 抽象组件(Component):定义公共接口(如显示、增删子节点)与属性。C语言中用“结构体+函数指针”模拟抽象层,使叶子与容器节点统一实现接口。

  • 叶子节点(Leaf):无子女的单个对象(如文件、按钮),实现抽象接口,不适用的增删方法返回错误或空实现。

  • 容器节点(Composite):含子节点的组合对象(如目录、布局容器),实现抽象接口,同时维护子节点列表,提供增删逻辑。

  • 组合模式核心优势:①透明性:客户端无需判断节点类型,直接调用抽象接口;②可扩展性:新增节点类型只需实现抽象接口,无需修改现有代码,符合开闭原则。

    二、C语言实现:抽象组件+叶子/容器节点

    C语言无类和继承,采用“结构体嵌套+函数指针”模拟抽象组件,让叶子与容器节点嵌套抽象组件实现接口复用。实现分三步:定义抽象组件、实现叶子节点、实现容器节点,最后通过递归遍历演示功能,代码可直接复用。

    2.1 第一步:定义抽象组件(Component)

    抽象组件是核心,定义公共接口(显示、增删子节点、销毁)与属性(名称)。无论文件、目录还是按钮、布局,均需名称标识,接口则统一操作逻辑。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>

    // 向前声明,因为抽象组件的函数指针需要用到容器节点结构体
    typedef struct Composite Composite;
    // 抽象组件结构体:定义公共接口和属性
    typedef struct Component {
    char name[32]; // 公共属性:名称
    // 函数指针:公共接口
    void (*show)(struct Component* self, int depth); // 显示(depth:层级,用于树形展示)
    int (*add)(struct Component* self, struct Component* child); // 添加子节点
    int (*remove)(struct Component* self, struct Component* child); // 删除子节点
    void (*destroy)(struct Component* self); // 销毁资源
    // 区分节点类型:0-叶子节点,1-容器节点(C语言无RTTI,手动标记)
    int is_composite;
    // 容器节点专属:子节点列表(叶子节点此指针为NULL)
    Composite* composite;
    } Component;

    说明:is_composite用于区分节点类型(C无RTTI),递归遍历需此标记;composite仅容器节点使用,指向子节点列表,叶子节点置空。

    2.2 第二步:实现叶子节点(Leaf)

    叶子节点无子女,增删方法返回错误,核心实现显示与销毁。以文件系统的“文件”为例,实现代码如下:

    // 叶子节点:文件(无子节点)
    void file_show(Component* self, int depth) {
    for (int i = 0; i < depth; i++) printf(" ");
    printf("文件:%s\\n", self->name);
    }

    int file_add(Component* self, Component* child) {
    printf("错误:文件%s不支持添加子节点!\\n", self->name);
    return 1;
    }

    int file_remove(Component* self, Component* child) {
    printf("错误:文件%s无可用子节点可删除!\\n", self->name);
    return 1;
    }

    void file_destroy(Component* self) {
    free(self); // 叶子节点无额外资源,直接释放
    }

    // 创建文件的工厂函数
    Component* create_file(const char* name) {
    Component* file = (Component*)malloc(sizeof(Component));
    if (file == NULL) return NULL;
    strncpy(file->name, name, sizeof(file->name)1);
    // 绑定接口
    file->show = file_show;
    file->add = file_add;
    file->remove = file_remove;
    file->destroy = file_destroy;
    file->is_composite = 0;
    file->composite = NULL;
    return file;
    }

    2.3 第三步:实现容器节点(Composite)

    容器节点需管理子节点,先定义子节点链表,再实现所有抽象接口(增删子节点、递归显示/销毁)。以文件系统的“目录”为例,实现代码如下:

    // 容器节点:目录(支持子节点)
    // 子节点链表结构
    typedef struct ChildNode {
    Component* child;
    struct ChildNode* next;
    } ChildNode;

    // 容器专属数据
    struct Composite {
    ChildNode* head; // 链表头
    int child_count; // 子节点数量,提升查询效率
    };

    // 递归显示目录及子节点
    void directory_show(Component* self, int depth) {
    for (int i = 0; i < depth; i++) printf(" ");
    printf("目录:%s\\n", self->name);
    ChildNode* node = self->composite->head;
    while (node != NULL) {
    node->child->show(node->child, depth + 1);
    node = node->next;
    }
    }

    // 添加子节点
    int directory_add(Component* self, Component* child) {
    if (self == NULL || child == NULL) return 1;
    ChildNode* new_node = (ChildNode*)malloc(sizeof(ChildNode));
    if (new_node == NULL) return 1;
    new_node->child = child;
    new_node->next = NULL;
    // 插入链表尾部
    if (self->composite->head == NULL) {
    self->composite->head = new_node;
    } else {
    ChildNode* temp = self->composite->head;
    while (temp->next != NULL) temp = temp->next;
    temp->next = new_node;
    }
    self->composite->child_count++;
    return 0;
    }

    // 删除子节点
    int directory_remove(Component* self, Component* child) {
    if (self == NULL || child == NULL || self->composite->head == NULL) return 1;
    ChildNode* prev = NULL, *curr = self->composite->head;
    while (curr != NULL) {
    if (strcmp(curr->child->name, child->name) == 0) {
    prev ? (prev->next = curr->next) : (self->composite->head = curr->next);
    free(curr);
    self->composite->child_count;
    return 0;
    }
    prev = curr;
    curr = curr->next;
    }
    printf("错误:目录%s中未找到子节点%s!\\n", self->name, child->name);
    return 1;
    }

    // 递归销毁目录及子节点
    void directory_destroy(Component* self) {
    if (self == NULL) return;
    ChildNode* curr = self->composite->head;
    while (curr != NULL) {
    ChildNode* temp = curr;
    curr = curr->next;
    temp->child->destroy(temp->child);
    free(temp);
    }
    free(self->composite);
    free(self);
    }

    // 创建目录的工厂函数
    Component* create_directory(const char* name) {
    Component* dir = (Component*)malloc(sizeof(Component));
    if (dir == NULL) return NULL;
    strncpy(dir->name, name, sizeof(dir->name)1);
    // 初始化容器数据
    dir->composite = (Composite*)malloc(sizeof(Composite));
    if (dir->composite == NULL) { free(dir); return NULL; }
    dir->composite->head = NULL;
    dir->composite->child_count = 0;
    // 绑定接口
    dir->show = directory_show;
    dir->add = directory_add;
    dir->remove = directory_remove;
    dir->destroy = directory_destroy;
    dir->is_composite = 1;
    return dir;
    }

    2.4 测试:递归遍历树形结构

    创建简单文件系统结构测试:根目录下含“文档”目录和“笔记.txt”,“文档”目录含“工作计划.docx”和“技术方案.pdf”,通过抽象接口验证统一处理逻辑。

    int main() {
    // 创建节点
    Component* root = create_directory("根目录(/)");
    Component* doc_dir = create_directory("文档");
    Component* note_file = create_file("笔记.txt");
    Component* plan_file = create_file("工作计划.docx");
    Component* tech_file = create_file("技术方案.pdf");

    // 组合树形结构
    root->add(root, doc_dir);
    root->add(root, note_file);
    doc_dir->add(doc_dir, plan_file);
    doc_dir->add(doc_dir, tech_file);

    // 显示结构
    printf("=== 初始文件系统结构 ===\\n");
    root->show(root, 0);

    // 测试删除
    printf("\\n=== 删除文档目录下的工作计划.docx ===\\n");
    doc_dir->remove(doc_dir, plan_file);
    root->show(root, 0);

    // 测试叶子节点添加子节点(预期报错)
    printf("\\n=== 尝试给笔记.txt添加子节点 ===\\n");
    note_file->add(note_file, plan_file);

    // 销毁资源
    root->destroy(root);
    return 0;
    }

    运行结果验证了组合模式的统一性,树形结构显示及操作效果如下:

    === 初始文件系统结构 ===
    目录:根目录(/)
    目录:文档
    文件:工作计划.docx
    文件:技术方案.pdf
    文件:笔记.txt

    === 删除文档目录下的工作计划.docx ===
    目录:根目录(/)
    目录:文档
    文件:技术方案.pdf
    文件:笔记.txt

    === 尝试给笔记.txt添加子节点 ===
    错误:文件笔记.txt不支持添加子节点!

    三、实战场景:从文件系统到嵌入式GUI

    拓展嵌入式GUI高频场景——控件树形布局,验证组合模式通用性。其应用逻辑与文件系统一致,仅需替换节点具体实现。

    3.1 嵌入式GUI场景适配

    嵌入式GUI中,按钮、文本框为叶子节点(无子控件),布局容器为容器节点(含子控件)。复用抽象组件结构,修改节点实现即可适配,代码如下:

    // 叶子节点:按钮(无子控件)
    void button_show(Component* self, int depth) {
    for (int i = 0; i < depth; i++) printf(" ");
    printf("按钮:%s(可点击)\\n", self->name);
    }

    int button_add(Component* self, Component* child) {
    printf("错误:按钮%s不支持添加子控件!\\n", self->name);
    return 1;
    }

    int button_remove(Component* self, Component* child) {
    printf("错误:按钮%s无可用子控件可删除!\\n", self->name);
    return 1;
    }

    void button_destroy(Component* self) { free(self); }

    // 创建按钮
    Component* create_button(const char* name) {
    Component* btn = (Component*)malloc(sizeof(Component));
    if (btn == NULL) return NULL;
    strncpy(btn->name, name, sizeof(btn->name)1);
    btn->show = button_show;
    btn->add = button_add;
    btn->remove = button_remove;
    btn->destroy = button_destroy;
    btn->is_composite = 0;
    btn->composite = NULL;
    return btn;
    }

    // 容器节点:垂直布局
    void vlayout_show(Component* self, int depth) {
    for (int i = 0; i < depth; i++) printf(" ");
    printf("垂直布局:%s\\n", self->name);
    ChildNode* node = self->composite->head;
    while (node != NULL) {
    node->child->show(node->child, depth + 1);
    node = node->next;
    }
    }

    // 复用目录的增删、销毁逻辑,修改函数名即可
    int vlayout_add(Component* self, Component* child) { /* 同directory_add */ }
    int vlayout_remove(Component* self, Component* child) { /* 同directory_remove */ }
    void vlayout_destroy(Component* self) { /* 同directory_destroy */ }

    // 创建垂直布局
    Component* create_vlayout(const char* name) {
    Component* layout = (Component*)malloc(sizeof(Component));
    if (layout == NULL) return NULL;
    strncpy(layout->name, name, sizeof(layout->name)1);
    layout->composite = (Composite*)malloc(sizeof(Composite));
    if (layout->composite == NULL) { free(layout); return NULL; }
    layout->composite->head = NULL;
    layout->composite->child_count = 0;
    layout->show = vlayout_show;
    layout->add = vlayout_add;
    layout->remove = vlayout_remove;
    layout->destroy = vlayout_destroy;
    layout->is_composite = 1;
    return layout;
    }

    3.2 测试GUI布局组合

    以登录页面布局测试:主垂直布局含“用户名文本框”“密码文本框”“登录按钮”,通过组合模式统一管理。

    int main() {
    Component* main_layout = create_vlayout("登录页面主布局");
    Component* user_text = create_button("用户名文本框");
    Component* pwd_text = create_button("密码文本框");
    Component* login_btn = create_button("登录按钮");

    // 组合布局
    main_layout->add(main_layout, user_text);
    main_layout->add(main_layout, pwd_text);
    main_layout->add(main_layout, login_btn);

    // 显示布局
    printf("=== 登录页面GUI布局 ===\\n");
    main_layout->show(main_layout, 0);

    // 销毁资源
    main_layout->destroy(main_layout);
    return 0;
    }

    运行可清晰显示树形布局,后续添加“忘记密码”按钮只需调用main_layout->add,无需修改现有代码,扩展性极强。

    四、递归设计要点与性能优化

    组合模式依赖树形结构,遍历需递归。递归简洁但在嵌入式场景易出现栈溢出、性能问题,以下是设计要点与优化技巧。

    4.1 递归设计要点

  • 明确递归终止条件:如叶子节点无子女,调用show接口不递归,避免栈溢出(嵌入式栈空间有限)。

  • 统一接口参数:如show接口的depth参数,确保递归传递准确,避免逻辑混乱。

  • 递归销毁资源:容器节点destroy需先销毁所有子节点,避免嵌入式系统内存泄漏。

  • 4.2 性能优化技巧

  • 迭代替代深层递归:深层嵌套时,用自定义栈/队列模拟递归,避免栈溢出。

  • 缓存子节点数量:child_count属性可快速获取子节点数,无需遍历链表,减少CPU占用。

  • 预分配内存池:避免频繁malloc/free导致嵌入式系统内存碎片,提升稳定性。

  • 缓存遍历结果:多次遍历树形结构时,缓存首次结果,避免重复递归,提升查询性能。

  • 五、总结与互动引导

    本文从C开发者实战视角,讲清组合模式“树形结构统一单个与组合对象”的核心,通过“抽象组件+叶子/容器节点”实现文件系统、嵌入式GUI场景落地,分享递归设计与优化技巧,代码可直接复用。

    组合模式在嵌入式C开发中应用广泛,除文件系统、GUI,还可用于传感器网络、任务管理等场景。掌握它可简化代码结构,提升项目扩展性与可维护性。

    若本文对你有帮助,欢迎点赞、收藏、关注!后续将分享更多设计模式的C语言实现及嵌入式应用案例,助力优化代码、提升效率。

    若实现过程有疑问,或有其他设计模式需求,欢迎在评论区留言讨论!

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 创建型模式:组合模式(C语言实现与嵌入式实战)
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!