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.代码解析
- 抽象接口 (
FileItem
) 定义了所有文件项共有的属性。 - 具体类 (
File
,Directory
) 分别实现了文件和目录的功能。 - 添加功能 (
addSubDirectory
,addFile
) 允许向目录中添加子目录或文件。 - 计算大小 (
calculateSize
) 方法用于计算文件或目录的大小。 - 打印方法 (
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
包含两个子目录subDir1
和subDir2
,以及两个文件file1.txt
和file2.txt
。 - 子目录
subDir1
包含一个文件subFile1.txt
。 - 子目录
subDir2
包含一个文件subFile2.txt
。 - 每个文件和目录都有自己的名称和大小。
- 根目录的大小为 0 字节,因为目录本身不占用空间,只表示一个容器。
- 文件的大小直接给出。
- 目录的总大小是所有子目录和文件的大小之和。
- 最后,我们计算出整个目录树的总大小为 4568 字节:
file1.txt
: 1024 字节file2.txt
: 2048 字节subFile1.txt
: 512 字节subFile2.txt
: 1024 字节- 总计: 4568 字节
这就是组合模式在文件系统中的应用,它可以让你以统一的方式处理文件和目录,无论它们是在根目录还是嵌套在多个层级之下。
4.总结
组合模式是一种结构型设计模式,它的主要目的是将对象组合成树形结构以表示“部分-整体”的层次结构。这种模式使得客户端能够一致地处理单个对象和组合对象。
关键点
- 抽象接口:定义了所有对象(无论是单个对象还是组合对象)的共同行为。
- 叶子对象:表示组成结构的基本组件,不能包含其他对象。
- 复合对象:可以包含其他对象(包括叶子对象和其他复合对象),形成树状结构。
- 递归结构:复合对象可以嵌套包含更多的复合对象,形成多层次的结构。
- 统一处理:客户端可以使用相同的接口来处理叶子对象和复合对象。
适用场景
- 当你需要表示对象之间的层次结构时。
- 当你想让客户端能够一致地处理单个对象和复合对象时。
- 当你想通过递归地访问复合对象来简化算法时。
优势
- 易于扩展:可以轻松地添加新的叶子对象或复合对象类型。
- 易于使用:客户端不需要区分单个对象和复合对象。
- 代码简洁:减少了重复代码,提高了代码的可读性和可维护性。
示例
- 文件系统:文件和目录的组织。
- GUI 组件:窗口、按钮等的布局。
- 多级菜单:网站或应用程序中的菜单结构。
组合模式提供了一种优雅的方式来构建和处理层次结构,它通过将对象组合成树形结构来简化客户端的处理逻辑。这种模式非常适合于需要表达“部分-整体”关系的场景,同时也便于扩展和维护。