用C语言实现组合模式

组合模式(Composite Pattern)的核心是将对象组合成树形结构,统一处理单个对象和组合对象 ,使客户端无需区分两者,可用一致的方式操作。在C语言中,可以通过结构体继承(模拟统一接口)+ 链表/数组(管理子对象) 实现:定义"组件"接口,叶子节点(单个对象)和容器节点(组合对象)都实现该接口,容器节点可包含子节点(叶子或其他容器)。

C语言实现组合模式的思路

  1. 抽象组件(Component) :定义所有节点(叶子和容器)的统一接口(如operationaddremove等函数指针)。
  2. 叶子节点(Leaf) :实现组件接口,代表不可再分的单个对象(无add/remove功能)。
  3. 容器节点(Composite):实现组件接口,内部包含子组件列表(叶子或其他容器),可管理子节点并递归调用其方法。

示例:文件系统(文件和文件夹的树形结构)

文件系统是组合模式的经典场景:

  • 文件夹(Folder):容器节点,可包含文件或其他文件夹。
  • 文件(File):叶子节点,不可包含子节点。
    两者都支持"显示信息"和"计算大小"的操作,客户端可统一处理。
步骤1:定义抽象组件(文件系统节点接口)
c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 抽象组件:文件系统节点接口
typedef struct FSComponent {
    char name[64]; // 节点名称(文件/文件夹名)
    
    // 统一操作:显示节点信息(递归显示子节点)
    void (*display)(struct FSComponent* self, int depth); // depth:缩进深度,用于树形显示
    // 统一操作:计算节点大小(文件大小/文件夹总大小)
    int (*get_size)(struct FSComponent* self);
    
    // 容器节点特有:添加子节点(叶子节点可设为NULL)
    void (*add)(struct FSComponent* self, struct FSComponent* child);
    // 容器节点特有:移除子节点
    void (*remove)(struct FSComponent* self, struct FSComponent* child);
    
    // 用于链表管理子节点(仅容器节点使用)
    struct FSComponent* next; // 下一个兄弟节点
} FSComponent;
步骤2:实现叶子节点(文件,File)

文件是不可再分的叶子节点,不支持add/remove,这两个方法可设为NULL或提示错误。

c 复制代码
// 叶子节点:文件
typedef struct {
    FSComponent component; // 继承抽象组件
    int size;              // 文件大小(KB)
} File;

// 文件的显示方法:打印文件名和大小
static void file_display(FSComponent* self, int depth) {
    // 打印缩进(树形结构)
    for (int i = 0; i < depth; i++) printf("  ");
    printf("- 文件: %s (大小: %dKB)\n", self->name, ((File*)self)->size);
}

// 文件的大小计算:直接返回自身大小
static int file_get_size(FSComponent* self) {
    return ((File*)self)->size;
}

// 创建文件(叶子节点)
FSComponent* file_create(const char* name, int size) {
    File* file = (File*)malloc(sizeof(File));
    if (!file) return NULL;
    
    // 初始化组件接口
    strncpy(file->component.name, name, sizeof(file->component.name)-1);
    file->component.display = file_display;
    file->component.get_size = file_get_size;
    file->component.add = NULL; // 叶子节点不支持添加子节点
    file->component.remove = NULL;
    file->component.next = NULL;
    
    // 初始化文件特有属性
    file->size = size;
    
    return (FSComponent*)file;
}
步骤3:实现容器节点(文件夹,Folder)

文件夹可包含多个子节点(文件或其他文件夹),通过链表管理子节点,并递归调用子节点的方法。

c 复制代码
// 容器节点:文件夹
typedef struct {
    FSComponent component;   // 继承抽象组件
    FSComponent* children;   // 子节点链表(头指针)
} Folder;

// 文件夹的显示方法:先显示自身,再递归显示所有子节点
static void folder_display(FSComponent* self, int depth) {
    Folder* folder = (Folder*)self;
    // 打印自身信息
    for (int i = 0; i < depth; i++) printf("  ");
    printf("+ 文件夹: %s\n", self->name);
    
    // 递归显示子节点(深度+1,增加缩进)
    FSComponent* child = folder->children;
    while (child) {
        child->display(child, depth + 1);
        child = child->next;
    }
}

// 文件夹的大小计算:递归累加所有子节点的大小
static int folder_get_size(FSComponent* self) {
    Folder* folder = (Folder*)self;
    int total_size = 0;
    FSComponent* child = folder->children;
    while (child) {
        total_size += child->get_size(child); // 递归计算子节点大小
        child = child->next;
    }
    return total_size;
}

// 文件夹添加子节点(链表头部插入)
static void folder_add(FSComponent* self, FSComponent* child) {
    if (!child) return;
    Folder* folder = (Folder*)self;
    // 新节点的next指向原头节点
    child->next = folder->children;
    // 头指针指向新节点
    folder->children = child;
}

// 文件夹移除子节点(从链表中删除)
static void folder_remove(FSComponent* self, FSComponent* child) {
    if (!child) return;
    Folder* folder = (Folder*)self;
    FSComponent* prev = NULL;
    FSComponent* curr = folder->children;
    
    // 查找子节点并从链表中删除
    while (curr) {
        if (curr == child) {
            if (prev) {
                prev->next = curr->next; // 前节点指向后节点
            } else {
                folder->children = curr->next; // 头节点更新
            }
            curr->next = NULL; // 断开被移除节点的链接
            break;
        }
        prev = curr;
        curr = curr->next;
    }
}

// 创建文件夹(容器节点)
FSComponent* folder_create(const char* name) {
    Folder* folder = (Folder*)malloc(sizeof(Folder));
    if (!folder) return NULL;
    
    // 初始化组件接口
    strncpy(folder->component.name, name, sizeof(folder->component.name)-1);
    folder->component.display = folder_display;
    folder->component.get_size = folder_get_size;
    folder->component.add = folder_add;
    folder->component.remove = folder_remove;
    folder->component.next = NULL;
    
    // 初始化子节点链表(空)
    folder->children = NULL;
    
    return (FSComponent*)folder;
}
步骤4:使用组合模式(统一操作树形结构)

客户端可通过抽象组件接口FSComponent统一操作文件和文件夹,无需区分类型。

c 复制代码
int main() {
    // 1. 创建叶子节点(文件)
    FSComponent* file1 = file_create("笔记.txt", 10);
    FSComponent* file2 = file_create("图片.jpg", 200);
    FSComponent* file3 = file_create("数据.csv", 50);
    
    // 2. 创建容器节点(文件夹)
    FSComponent* docs_folder = folder_create("文档");
    FSComponent* pics_folder = folder_create("图片");
    FSComponent* root_folder = folder_create("根目录");
    
    // 3. 组合树形结构
    docs_folder->add(docs_folder, file1);    // 文档文件夹添加笔记.txt
    docs_folder->add(docs_folder, file3);    // 文档文件夹添加数据.csv
    pics_folder->add(pics_folder, file2);    // 图片文件夹添加图片.jpg
    root_folder->add(root_folder, docs_folder); // 根目录添加文档文件夹
    root_folder->add(root_folder, pics_folder); // 根目录添加图片文件夹
    
    // 4. 统一操作:显示整个树形结构(从根目录开始)
    printf("文件系统结构:\n");
    root_folder->display(root_folder, 0); // depth=0(无缩进)
    
    // 5. 统一操作:计算总大小
    printf("\n根目录总大小: %dKB\n", root_folder->get_size(root_folder));
    
    // 6. 清理内存(简化处理,实际需递归释放所有节点)
    free(file1);
    free(file2);
    free(file3);
    free(docs_folder);
    free(pics_folder);
    free(root_folder);
    
    return 0;
}

输出结果

复制代码
文件系统结构:
+ 文件夹: 根目录
  + 文件夹: 图片
    - 文件: 图片.jpg (大小: 200KB)
  + 文件夹: 文档
    - 文件: 数据.csv (大小: 50KB)
    - 文件: 笔记.txt (大小: 10KB)

根目录总大小: 260KB

核心思想总结

  1. 树形结构统一处理 :无论是单个文件(叶子)还是文件夹(容器),客户端都通过FSComponent接口操作,无需区分类型(如displayget_size对两者都适用)。
  2. 递归组合 :容器节点可包含其他容器节点(如"根目录"包含"文档"文件夹),形成多级树形结构,且操作可递归传递(如文件夹的get_size会递归计算所有子节点大小)。
  3. 灵活性 :新增节点类型(如"压缩文件")时,只需实现FSComponent接口,现有客户端代码无需修改,符合开放-封闭原则

C语言通过结构体继承(FileFolder包含FSComponent)和链表管理子节点,完美模拟了组合模式的核心,适合处理树形结构场景(如文件系统、组织架构、UI组件树等)。

相关推荐
Yupureki4 小时前
从零开始的C++学习生活 12:AVL树全面解析
c语言·数据结构·c++·学习·visual studio
我是华为OD~HR~栗栗呀4 小时前
华为od面经-23届-Java面经
java·c语言·c++·python·华为od·华为·面试
逐步前行7 小时前
C标准库--C99--布尔型<stdbool.h>
c语言·开发语言
SunnyKriSmile7 小时前
C语言译码操作
c语言·算法·if语句·译码操作·switch语句
傻童:CPU7 小时前
C语言需要掌握的基础知识点之线性表
c语言·1024程序员节
Heavy sea7 小时前
Linux串口应用编程
linux·c语言·1024程序员节
再睡一夏就好7 小时前
【C++闯关笔记】详解多态
c语言·c++·笔记·学习·语法·1024程序员节
奔跑吧邓邓子11 小时前
【C语言实战(44)】C语言打造全能简易计算器:突破运算极限
c语言·实战·全能简易计算器
杯莫停丶12 小时前
设计模式之:组合模式
设计模式·组合模式