10设计模式_组合模式_C语言实现

组合模式

1 简介

组合模式将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

组合模式非常好理解。它应用于具有树形结构的对象组合。

优点就是:用一套调用方式处理对象组合中各个对象,且允许自由地增加对象个数。

2 模拟场景

文件系统的目录结构解释一个非常典型的目录结构。

本文假设有如下形式的文件系统,并以此为例:

在这里插入图片描述

3 使用组合模式实现文件系统

参与者

  1. Component: FileSystemNode

为文件系统中的节点声明接口

  1. Leaf: File

文件对象,在文件系统中,文件对象是文件系统的叶子对象(即:不可能再为文件对象添加子节点)

  1. Composite:Folder

文件夹对象

UML

在这里插入图片描述

FileSystemNode示例代码

file_system_node.h

#ifndef FILE_SYSTEM_NODE_H
#define FILE_SYSTEM_NODE_H

struct FileSystemNode {
    char nodeName[100];
    struct FileSystemNode *childList[100];
    void (*Operation)(struct FileSystemNode *this);
    void (*Add)(struct FileSystemNode *this, struct FileSystemNode *node);
    void (*Remove)(struct FileSystemNode *this, struct FileSystemNode *node);
};

#endif

File示例代码

file.h

#ifndef FILE_H
#define FILE_H

#include "file_system_node.h"

// 构造函数
void File(struct FileSystemNode *this, char *nodeName);
// 析构函数
void _File(struct FileSystemNode *this);

#endif

file.c

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

static void Operation(struct FileSystemNode *this)
{   
    printf("操作文件 %s\n", this->nodeName);
}

static void Add(struct FileSystemNode *this, struct FileSystemNode *node)
{    
    printf("error: 文件节点,不支持增加子节点\n");
}

static void Remove(struct FileSystemNode *this, struct FileSystemNode *node)
{
    printf("error: 文件节点,不支持删除子节点\n");
}

// 构造函数
void File(struct FileSystemNode *this, char *nodeName)
{
    strcpy(this->nodeName, nodeName);
    memset(this->childList, 0, sizeof(this->childList));
    this->Operation = Operation;
    this->Add = Add;
    this->Remove = Remove;
}

// 析构函数
void _File(struct FileSystemNode *this)
{
    memset(this->nodeName, 0, sizeof(this->nodeName));
    memset(this->childList, 0, sizeof(this->childList));
    this->Operation = NULL;
    this->Add = NULL;
    this->Remove = NULL;
}

Folder示例代码

folder.h

#ifndef FOLDER_H
#define FOLDER_H

#include "file_system_node.h"

// 构造函数
void Folder(struct FileSystemNode *this, char *nodeName);
// 析构函数
void _Folder(struct FileSystemNode *this);

#endif

folder.c

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

static void Operation(struct FileSystemNode *this)
{    
    printf("操作文件夹 %s\n", this->nodeName);
    
    int i;      
    for (i = 0; i < 100; i++) {
        if (this->childList[i] != NULL) {
            this->childList[i]->Operation(this->childList[i]);
        }        
    }
}

static void Add(struct FileSystemNode *this, struct FileSystemNode *node)
{
    int i;      
    for (i = 0; i < 100; i++) {
        if (this->childList[i] == NULL) {
            this->childList[i] = node;
            return;
        }
    }
    printf("error: add node fail\n");
}

static void Remove(struct FileSystemNode *this, struct FileSystemNode *node)
{
    int i;      
    for (i = 0; i < 100; i++) {
        if (this->childList[i] == node) {
            this->childList[i] = NULL;
            return;
        }
    }
    printf("error: remove node fail\n");
}

// 构造函数
void Folder(struct FileSystemNode *this, char *nodeName)
{
    strcpy(this->nodeName, nodeName);
    memset(this->childList, 0, sizeof(this->childList));
    this->Operation = Operation;
    this->Add = Add;
    this->Remove = Remove;
}

// 析构函数
void _Folder(struct FileSystemNode *this)
{
    memset(this->nodeName, 0, sizeof(this->nodeName));
    memset(this->childList, 0, sizeof(this->childList));
    this->Operation = NULL;
    this->Add = NULL;
    this->Remove = NULL;
}

客户端代码示例

#include "folder.h"
#include "file.h"

void main()
{
    struct FileSystemNode root;
    struct FileSystemNode folder1;
    struct FileSystemNode folder2;
    struct FileSystemNode folder3;
    struct FileSystemNode file1;
    struct FileSystemNode file2;
    struct FileSystemNode file3;

    Folder(&root, "root");
    Folder(&folder1, "folder1");
    Folder(&folder2, "folder2");
    Folder(&folder3, "folder3");
    File(&file1, "file1");
    File(&file2, "file2");
    File(&file3, "file3");

    root.Add(&root, &folder1);
    root.Add(&root, &folder2);
    root.Add(&root, &file1);
    folder1.Add(&folder1, &file2);
    folder1.Add(&folder1, &file3);
    folder2.Add(&folder2, &folder3);

    root.Operation(&root);
}

客户端显示示例

-bash-4.2# ./test
操作文件夹 root
操作文件夹 folder1
操作文件 file2
操作文件 file3
操作文件夹 folder2
操作文件夹 folder3
操作文件 file1

4 组合模式的透明性与安全性分析

从上面的UML和示例代码不难发现,对于File,Add()和Remove()是没有意义的,甚至可以说是错误的。虽然我们为File实现的Add()和Remove()为空,并打印了错误信息,但这仍然可能引入错误(企图向File增加子节点本身就是一种错误)。

那么,如何解决这个问题呢?

解决思路是:既然File不需要Add()和Remove(),那我们就应该将Add()和Remove()从FileSystemNode中移到Folder中

但这又带来了一个新的问题:客户端调用File和Folder时不能采用完全一致的方法,因为他们提供的方法并不完全一致。

上述两种方案各有优势,但都不完美。没有一个同时解决上述两个问题的完美方案

对于前一种方案,因为具有透明性(客户端可以不区分File和Folder),我们称其为透明的组合模式。

对于后一种方案,因为更加安全(客户端不会错误地调用无意义的方法),我们称其为安全的组合模式。

通常我们根据场景对透明性和安全性的要求,以及个人喜好,来选择方案。

以下给出安全的组合模式的UML,以便和透明的组合模式进行对比:

在这里插入图片描述

使用安全的组合模式时,客户端需要通过GetType(),获知节点是File还是Folder,只有当节点为Folder是才能调用Add()和Remove()。

猜你喜欢

转载自blog.csdn.net/weixin_46826913/article/details/107305421