10設計パターン_組み合わせパターン_ C言語実装

組み合わせモード

1はじめに

結合モードは、オブジェクトをツリー構造に結合して、「部分全体」の階層構造を表します。結合モードでは、ユーザーは単一オブジェクトと結合オブジェクトの使用に一貫性を持たせることができます。

組み合わせモードは非常に理解しやすいです。オブジェクトとツリー構造の組み合わせに適用されます。

利点は、一連の呼び出しメソッドを使用してオブジェクトの組み合わせの各オブジェクトを処理し、オブジェクトの数を自由に増やすことができることです。

2シミュレーションシーン

ファイルシステムのディレクトリ構造は、非常に典型的なディレクトリ構造を説明しています。

この記事では、次の形式のファイルシステムを想定し、これを例として使用します。

ここに画像の説明を挿入

3複合モードを使用してファイルシステムを実装する

参加者

  1. コンポーネント:FileSystemNode

ファイルシステムのノードのインターフェイスを宣言する

  1. リーフ:ファイル

ファイルオブジェクトファイルシステムでは、ファイルオブジェクトはファイルシステムのリーフオブジェクトです(つまり、子ノードをファイルオブジェクトに追加することはできません)。

  1. コンポジット:フォルダー

フォルダオブジェクト

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.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.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を呼び出すときにまったく同じメソッドを使用できません

上記の2つのスキームにはそれぞれ独自の利点がありますが、完全ではありません。上記の問題の両方に対する完全な解決策はありません

前者のスキームでは、その透過性のため(クライアントはファイルとフォルダーを区別できない)、透過的な組み合わせモードと呼びます。

後者の方式では、より安全であるため(クライアントが無意味なメソッドを誤って呼び出さないため)、これをセーフコンビネーションモードと呼びます。

通常、透明性とセキュリティに関するシーンの要件、および個人的な好みに基づいてソリューションを選択します。

以下は、透過コンビネーションモードと比較するためのセーフコンビネーションモードのUMLを示しています。

ここに画像の説明を挿入

セーフコンビネーションモードを使用する場合、クライアントはGetType()によってノードがファイルであるかフォルダであるかを知る必要があり、ノードがフォルダである場合にのみAdd()およびRemove()を呼び出すことができます。

おすすめ

転載: blog.csdn.net/weixin_46826913/article/details/107305421