设计模式专题之C语言-组合模式

1.简介

组合模式是一种结构型设计模式,它允许你将对象组合成树形结构以表示部分-整体层次结构。组合模式使得客户端能够一致地处理单个对象和组合对象。这种模式在需要处理具有层次结构的数据时非常有用。

2.通俗讲解

假设我们正在为一个文件管理器编写代码,该管理器可以显示目录树,并且用户可以在其中创建文件夹或文件。我们需要能够递归地遍历整个目录树并计算所有文件的总大小。

3.实战

设计

首先,我们需要定义一个抽象的接口 FileItem,这个接口既适用于文件也适用于文件夹。然后,我们将创建两个具体的类,一个代表文件 File,另一个代表文件夹 Directory。文件夹可以包含其他 FileItem 对象。

3.1.代码

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

// 抽象接口 FileItem
typedef struct FileItem {
    char name[50];
    int size;
} FileItem;

// 具体类 File
typedef struct File {
    FileItem fileItem;
} File;

// 具体类 Directory
typedef struct Directory {
    FileItem directoryItem;
    struct Directory **subDirectories;
    File **files;
    int subDirectoryCount;
    int fileCount;
    int maxSubDirectories;
    int maxFiles;
} Directory;

// 创建文件
File* createFile(const char *name, int size) {
    File *file = malloc(sizeof(File));
    strcpy(file->fileItem.name, name);
    file->fileItem.size = size;
    return file;
}

// 创建目录
Directory* createDirectory(const char *name) {
    Directory *directory = malloc(sizeof(Directory));
    strcpy(directory->directoryItem.name, name);
    directory->subDirectoryCount = 0;
    directory->fileCount = 0;
    directory->maxSubDirectories = 10;
    directory->maxFiles = 10;
    directory->subDirectories = (Directory**)malloc(directory->maxSubDirectories * sizeof(Directory*));
    directory->files = (File*)malloc(directory->maxFiles * sizeof(File));
    return directory;
}

// 添加子目录
void addSubDirectory(Directory *directory, Directory *subDirectory) {
    if (directory->subDirectoryCount >= directory->maxSubDirectories) {
        directory->maxSubDirectories *= 2;
        directory->subDirectories = realloc(directory->subDirectories, directory->maxSubDirectories * sizeof(Directory*));
    }
    directory->subDirectories[directory->subDirectoryCount++] = subDirectory;
}

// 添加文件
void addFile(Directory *directory, File *file) {
    if (directory->fileCount >= directory->maxFiles) {
        directory->maxFiles *= 2;
        directory->files = realloc(directory->files, directory->maxFiles * sizeof(File));
    }
    directory->files[directory->fileCount++] = file;
}

// 计算文件大小
int calculateSize(FileItem *item) {
    return item->size;
}

// 计算目录及其内容的总大小
int calculateSize(Directory *directory) {
    int totalSize = directory->directoryItem.size;
    for (int i = 0; i < directory->subDirectoryCount; i++) {
        totalSize += calculateSize(directory->subDirectories[i]);
    }
    for (int i = 0; i < directory->fileCount; i++) {
        totalSize += calculateSize(&(directory->files[i]->fileItem));
    }
    return totalSize;
}

// 打印文件信息
void printFileItem(FileItem *item, int level) {
    for (int i = 0; i < level; i++) printf("  ");
    printf("%s (%d bytes)\n", item->name, item->size);
}

// 打印目录及其内容
void printDirectory(Directory *directory, int level) {
    printFileItem(&(directory->directoryItem), level);
    for (int i = 0; i < directory->subDirectoryCount; i++) {
        printDirectory(directory->subDirectories[i], level + 1);
    }
    for (int i = 0; i < directory->fileCount; i++) {
        printFileItem(&(directory->files[i]->fileItem), level + 1);
    }
}

// 主函数
int main() {
    Directory *root = createDirectory("root");
    File *file1 = createFile("file1.txt", 1024);
    File *file2 = createFile("file2.txt", 2048);
    
    Directory *subDir1 = createDirectory("subDir1");
    Directory *subDir2 = createDirectory("subDir2");
    File *subFile1 = createFile("subFile1.txt", 512);
    File *subFile2 = createFile("subFile2.txt", 1024);

    addFile(subDir1, subFile1);
    addFile(subDir2, subFile2);
    addSubDirectory(root, subDir1);
    addSubDirectory(root, subDir2);
    addFile(root, file1);
    addFile(root, file2);

    printf("Directory tree:\n");
    printDirectory(root, 0);

    int totalSize = calculateSize(root);
    printf("Total size: %d bytes\n", totalSize);

    // 清理资源
    free(file1);
    free(file2);
    free(subFile1);
    free(subFile2);
    free(subDir1);
    free(subDir2);
    free(root);

    return 0;
}

3.2.代码解析

  1. 抽象接口 (FileItem) 定义了所有文件项共有的属性。
  2. 具体类 (File, Directory) 分别实现了文件和目录的功能。
  3. 添加功能 (addSubDirectory, addFile) 允许向目录中添加子目录或文件。
  4. 计算大小 (calculateSize) 方法用于计算文件或目录的大小。
  5. 打印方法 (printFileItem, printDirectory) 用于展示文件和目录的信息。

通过这种方式,我们可以灵活地创建和管理复杂的目录树结构,并能以统一的方式处理这些结构中的元素。

3.3.代码运行

Directory tree:
root (0 bytes)
  subDir1 (0 bytes)
    subFile1.txt (512 bytes)
  subDir2 (0 bytes)
    subFile2.txt (1024 bytes)
  file1.txt (1024 bytes)
  file2.txt (2048 bytes)
Total size: 4568 bytes

3.4.结果分析

  • 根目录 root 包含两个子目录 subDir1subDir2,以及两个文件 file1.txtfile2.txt
  • 子目录 subDir1 包含一个文件 subFile1.txt
  • 子目录 subDir2 包含一个文件 subFile2.txt
  • 每个文件和目录都有自己的名称和大小。
  • 根目录的大小为 0 字节,因为目录本身不占用空间,只表示一个容器。
  • 文件的大小直接给出。
  • 目录的总大小是所有子目录和文件的大小之和。
  • 最后,我们计算出整个目录树的总大小为 4568 字节:
    • file1.txt: 1024 字节
    • file2.txt: 2048 字节
    • subFile1.txt: 512 字节
    • subFile2.txt: 1024 字节
    • 总计: 4568 字节

这就是组合模式在文件系统中的应用,它可以让你以统一的方式处理文件和目录,无论它们是在根目录还是嵌套在多个层级之下。

4.总结

组合模式是一种结构型设计模式,它的主要目的是将对象组合成树形结构以表示“部分-整体”的层次结构。这种模式使得客户端能够一致地处理单个对象和组合对象。

关键点

  • 抽象接口:定义了所有对象(无论是单个对象还是组合对象)的共同行为。
  • 叶子对象:表示组成结构的基本组件,不能包含其他对象。
  • 复合对象:可以包含其他对象(包括叶子对象和其他复合对象),形成树状结构。
  • 递归结构:复合对象可以嵌套包含更多的复合对象,形成多层次的结构。
  • 统一处理:客户端可以使用相同的接口来处理叶子对象和复合对象。

适用场景

  • 当你需要表示对象之间的层次结构时。
  • 当你想让客户端能够一致地处理单个对象和复合对象时。
  • 当你想通过递归地访问复合对象来简化算法时。

优势

  • 易于扩展:可以轻松地添加新的叶子对象或复合对象类型。
  • 易于使用:客户端不需要区分单个对象和复合对象。
  • 代码简洁:减少了重复代码,提高了代码的可读性和可维护性。

示例

  • 文件系统:文件和目录的组织。
  • GUI 组件:窗口、按钮等的布局。
  • 多级菜单:网站或应用程序中的菜单结构。

组合模式提供了一种优雅的方式来构建和处理层次结构,它通过将对象组合成树形结构来简化客户端的处理逻辑。这种模式非常适合于需要表达“部分-整体”关系的场景,同时也便于扩展和维护。

  • 15
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

甜航一直在

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值