【組合設計模式詳解】C/Java/JS/Go/Python/TS不同語言實現

来源:https://www.cnblogs.com/letjs/archive/2023/04/03/17283920.html
-Advertisement-
Play Games

簡介 組合模式(Composite Pattern),又叫部分整體模式,是一種結構型設計模式。用於把一組類似的對象當作一個單一的對象來看。組合模式依據樹形結構來組合對象,用不同組件來構建某個部分或整體對象。 如果你需要實現樹狀對象結構,可以使用組合模式。如果你希望客戶端代碼以相同方式處理簡單和複雜元 ...


簡介

組合模式(Composite Pattern),又叫部分整體模式,是一種結構型設計模式。用於把一組類似的對象當作一個單一的對象來看。組合模式依據樹形結構來組合對象,用不同組件來構建某個部分或整體對象。

如果你需要實現樹狀對象結構,可以使用組合模式。如果你希望客戶端代碼以相同方式處理簡單和複雜元素,可以使用該模式。

作用

  1. 符合開閉原則。無需更改現有代碼,就可以在應用中添加新元素,使之成為對象樹的一部分。
  2. 模糊了簡單元素和複雜元素的概念,程式可以像處理簡單元素一樣來處理複雜元素,從而使得程式與複雜元素的內部結構解耦。

實現步驟

  1. 創建抽象構件(Component)介面,用於聲明樹葉構件和樹枝構件的預設行為。
  2. 創建樹枝構件(Composite)角色 / 中間構件:是組合中的分支節點對象,它有子節點,用於繼承和實現抽象構件。它的主要作用是存儲和管理子部件,通常包含 Add()、Remove()、GetChild() 等方法。
  3. 定義樹葉構件(Leaf)角色:是組合中的葉子節點對象,它沒有子節點,用於繼承或實現抽象構件。

UML

 

Java語言代碼

基礎部件介面

// OrganizationComponent.java 定義部件介面或抽象類,分支和葉子節點遵循該類約定
public interface OrganizationComponent {
   public void add(OrganizationComponent component);

   public void remove(OrganizationComponent component);

   public OrganizationComponent getChild(int index);

   public void operation();

   public String getName();
}

 

具體部件實現

// CompanyComposite.java 實現部件的樹枝構件1
public class CompanyComposite implements OrganizationComponent {

   private String name;
   private List<OrganizationComponent> children = new ArrayList<OrganizationComponent>();

   public CompanyComposite(String name) {
      this.name = name;
   }

   public void add(OrganizationComponent component) {
      children.add(component);
   }

   public void remove(OrganizationComponent component) {
      children.remove(component);
   }

   public OrganizationComponent getChild(int index) {
      return children.get(index);
   }

   public void operation() {
      System.out.println(this.getClass().getName() + " CompanyComposite::operation() " + this.name);
      for (Object component : children) {
         ((OrganizationComponent) component).operation();
      }
   }

   public String getName() {
      return name;
   }
}
// DepartmentComposite.java 實現部件的樹枝構件2 public class DepartmentComposite implements OrganizationComponent { private String name; private List<OrganizationComponent> children = new ArrayList<OrganizationComponent>(); public DepartmentComposite(String name) { this.name = name; } public void add(OrganizationComponent component) { children.add(component); } public void remove(OrganizationComponent component) { children.remove(component); } public OrganizationComponent getChild(int index) { return children.get(index); } public void operation() { System.out.println(this.getClass().getName() + " DepartmentComposite::operation() " + this.name); for (Object component : children) { ((OrganizationComponent) component).operation(); } } public String getName() { return name; } }
// EmployeeLeaf.java 實現部件的葉子節點,葉子節點不能再含有子節點 public class EmployeeLeaf implements OrganizationComponent { private String name; public EmployeeLeaf(String name) { this.name = name; } // 葉子節點不能再增加內容 public void add(OrganizationComponent component) { System.out.println("Leaf can't add."); } // 葉子節點沒有移除內容 public void remove(OrganizationComponent component) { System.out.println("Leaf can't remove."); } // 葉子節點無獲取子節點 public OrganizationComponent getChild(int index) { System.out.println("Leaf can't getChild."); return null; } public void operation() { System.out.println(this.getClass().getName() + " EmployeeLeaf::operation() " + this.name); } public String getName() { return name; } }

 

測試調用

    /**
     * 組合模式依據樹形結構來組合對象,用不同組件來構建整體對象。
     * 不同組件之間有相同的介面約束,有不同的具體實現。
     * 先定義頂級節點,然後陸續加入枝葉節點和葉子節點,這樣不斷添加,將零散的個體組成一個整體。
     */

    // 通過組合模型組合了一個部件,分支和節點可以隨意增刪
    OrganizationComponent com = new CompanyComposite("西天旅游有限公司");
    OrganizationComponent com1 = new DepartmentComposite("總裁辦");
    OrganizationComponent com2 = new DepartmentComposite("行動隊");
    OrganizationComponent com3 = new DepartmentComposite("後勤組");
    OrganizationComponent leaf1 = new EmployeeLeaf("唐三藏");
    OrganizationComponent leaf2 = new EmployeeLeaf("孫悟空");
    OrganizationComponent leaf3 = new EmployeeLeaf("豬悟能");
    OrganizationComponent leaf4 = new EmployeeLeaf("沙悟凈");

    com.add(com1);
    com.add(com2);
    com.add(com3);

    // leaf1屬於com1
    com1.add(leaf1);
    // leaf2, leaf3屬於com2
    com2.add(leaf2);
    com2.add(leaf3);

    // 添加再刪除
    DepartmentComposite dept1 = new DepartmentComposite("小分隊");
    com2.add(dept1);
    EmployeeLeaf tmp1 = new EmployeeLeaf("臨時工");
    dept1.add(tmp1);
    dept1.remove(tmp1);

    // leaf4屬於com3
    com3.add(leaf4);

    // 執行全部節點動作
    com.operation();

    // 獲取某個節點
    OrganizationComponent employee = (EmployeeLeaf) com.getChild(1).getChild(0);
    System.out.println(employee.getName());

 

C語言代碼

func.h 自定義頭文件
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>

// 定義部件介面或抽象類,分支和葉子節點遵循該類約定
typedef struct OrganizationComponent
{
    char name[200];
    void (*add)(struct OrganizationComponent *, struct OrganizationComponent *);
    void (*remove)(struct OrganizationComponent *, struct OrganizationComponent *);
    struct OrganizationComponent *(*get_child)(struct OrganizationComponent *, int);
    void (*operation)(struct OrganizationComponent *);
    int children_size;
    struct OrganizationComponent **children;
    // 如果是柔型數組,則自動擴展數組長度,但可能導致出現亂碼現象,故採取固定長度數組
    // struct OrganizationComponent *children[];
} OrganizationComponent;
void add_component(OrganizationComponent *, OrganizationComponent *);
void remove_component(OrganizationComponent *, OrganizationComponent *);
OrganizationComponent *get_child_component(OrganizationComponent *, int);
void print_children(OrganizationComponent *children[], int children_size);

// 實現部件的樹枝構件1
typedef struct CompanyComposite
{
    char name[200];
    void (*add)(struct OrganizationComponent *, struct OrganizationComponent *);
    void (*remove)(struct OrganizationComponent *, struct OrganizationComponent *);
    struct OrganizationComponent *(*get_child)(struct OrganizationComponent *, int);
    void (*operation)(struct CompanyComposite *);
    int children_size;
    struct OrganizationComponent **children;
} CompanyComposite;
CompanyComposite *company_composite_constructor(char *name);

// 實現部件的樹枝構件2
typedef struct DepartmentComposite
{
    char name[200];
    void (*add)(struct OrganizationComponent *, struct OrganizationComponent *);
    void (*remove)(struct OrganizationComponent *, struct OrganizationComponent *);
    struct OrganizationComponent *(*get_child)(struct OrganizationComponent *, int);
    void (*operation)(struct DepartmentComposite *);
    int children_size;
    struct OrganizationComponent **children;
} DepartmentComposite;
DepartmentComposite *department_composite_constructor(char *name);

// 實現部件的葉子節點,葉子節點不能再含有子節點
typedef struct EmployeeLeaf
{
    char name[200];
    void (*add)(struct OrganizationComponent *, struct OrganizationComponent *);
    void (*remove)(struct OrganizationComponent *, struct OrganizationComponent *);
    struct OrganizationComponent *(*get_child)(struct OrganizationComponent *, int);
    void (*operation)(struct EmployeeLeaf *);
    int children_size;
    struct OrganizationComponent **children;
} EmployeeLeaf;
EmployeeLeaf *employee_leaf_constructor(char *name);

 

基礎部件介面

// organization_component.c 定義部件介面或抽象類,分支和葉子節點遵循該類約定
#include "func.h"

// 定義部件介面或抽象類,分支和葉子節點遵循該類約定
// C語言沒有介面和抽象類,用struct替代,同時把公共函數聲明在這裡

// 添加一個組件到子節點中
void add_component(OrganizationComponent *parent, OrganizationComponent *component)
{
  // 先將原數組保留下來
  OrganizationComponent **old_children = parent->children;
  parent->children_size += 1;
  // 新申請空間給子節點數組
  parent->children = (OrganizationComponent **)calloc(parent->children_size, sizeof(OrganizationComponent *));
  for (int i = 0; i < parent->children_size - 1; i++)
  {
    parent->children[i] = old_children[i];
  }
  // 將組件追加到子節點數組中
  parent->children[parent->children_size - 1] = component;
  free(old_children);
}

// 移除第一個匹配的子節點
void remove_component(OrganizationComponent *parent, OrganizationComponent *component)
{
  int size = parent->children_size;
  // 初始化組件id大於數組長度
  int com_idx = size;
  for (int i = 0; i < size; i++)
  {
    // 找到第一個匹配的組件下標
    if (parent->children[i] == component)
    {
      com_idx = i;
    }

    // 自匹配項開始,後項逐個往前移動1位
    if (i >= com_idx)
    {
      parent->children[i] = parent->children[i + 1];
      // 最後一項置空且總長度減少1位
      if (i == size - 1)
      {
        parent->children[i] = NULL;
        parent->children_size -= 1;
      }
    }
  }
}

// 列印全部子節點
void print_children(OrganizationComponent *children[], int children_size)
{
  for (int i = 0; i < children_size; i++)
  {
    printf("\r\n    children[%d]=%s", i, children[i]->name);
  }
}

// 根據下標獲取子節點
OrganizationComponent *get_child_component(OrganizationComponent *component, int index)
{
  return component->children[index];
}

 

具體部件實現

// company_composite.c 實現部件的樹枝構件1
#include "func.h"

// 實現部件的樹枝構件1
void company_composite_operation(CompanyComposite *component)
{
  printf("\r\n CompanyComposite::operation() [name=%s]", component->name);
  print_children(component->children, component->children_size);
  for (int i = 0; i < component->children_size; i++)
  {
    if (component->children[i] != NULL)
    {
      component->children[i]->operation(component->children[i]);
    }
  }
}

// 創建CompanyComposite對象
CompanyComposite *company_composite_constructor(char *name)
{
  OrganizationComponent *component = (OrganizationComponent *)malloc(sizeof(OrganizationComponent));
  strncpy(component->name, name, 200);
  component->add = &add_component;
  component->remove = &remove_component;
  component->children_size = 0;
  component->get_child = &get_child_component;
  // 轉為CompanyComposite
  CompanyComposite *company_composite = (CompanyComposite *)component;
  company_composite->operation = &company_composite_operation;
  return company_composite;
}

// department_composite.c 實現部件的樹枝構件2 #include "func.h" // 實現部件的樹枝構件2 void department_composite_operation(DepartmentComposite *component) { printf("\r\n DepartmentComposite::operation() [name=%s]", component->name); print_children(component->children, component->children_size); for (int i = 0; i < component->children_size; i++) { if (component->children[i] != NULL) { component->children[i]->operation(component->children[i]); } } } // 創建DepartmentComposite對象 DepartmentComposite *department_composite_constructor(char *name) { OrganizationComponent *component = (OrganizationComponent *)malloc(sizeof(OrganizationComponent)); strncpy(component->name, name, 200); component->add = &add_component; component->remove = &remove_component; component->children_size = 0; component->get_child = &get_child_component; // 轉為DepartmentComposite DepartmentComposite *department_composite = (DepartmentComposite *)component; department_composite->operation = &department_composite_operation; return department_composite; }

// employee_leaf.c 實現部件的葉子節點,葉子節點不能再含有子節點 #include "func.h" // 實現部件的葉子節點,葉子節點不能再含有子節點 // 葉子節點不能再增加內容 void add_leaf_component(OrganizationComponent *parent, OrganizationComponent *component) { printf("\r\n Leaf can't add."); } // 葉子節點沒有移除內容 void remove_leaf_component(OrganizationComponent *parent, OrganizationComponent *component) { printf("\r\n Leaf can't remove."); } // 葉子節點不能獲取子節點 OrganizationComponent *get_leaf_child_component(OrganizationComponent *component, int index) { printf("\r\n Leaf can't get_child."); return NULL; } // 子節點的操作函數 void employee_leaf_operation(EmployeeLeaf *component) { printf("\r\n EmployeeLeaf::operation() [name=%s]", component->name); } // 創建EmployeeLeaf對象 EmployeeLeaf *employee_leaf_constructor(char *name) { OrganizationComponent *component = (OrganizationComponent *)malloc(sizeof(OrganizationComponent)); strncpy(component->name, name, 200); component->children_size = 0; component->add = &add_leaf_component; component->remove = &remove_leaf_component; component->get_child = &get_leaf_child_component; // 轉為EmployeeLeaf EmployeeLeaf *employee_leaf = (EmployeeLeaf *)component; employee_leaf->operation = &employee_leaf_operation; return employee_leaf; }

 

測試調用

   /**
    * 組合模式依據樹形結構來組合對象,用不同組件來構建整體對象。
    * 不同組件之間有相同的介面約束,有不同的具體實現。
    * 先定義頂級節點,然後陸續加入枝葉節點和葉子節點,這樣不斷添加,將零散的個體組成一個整體。ss
    */

   // 通過組合模型組合了一個部件,分支和節點可以隨意增刪
   OrganizationComponent *com = (OrganizationComponent *)company_composite_constructor("西天旅游有限公司");
   OrganizationComponent *com1 = (OrganizationComponent *)department_composite_constructor("總裁辦");
   OrganizationComponent *com2 = (OrganizationComponent *)department_composite_constructor("行動隊");
   OrganizationComponent *com3 = (OrganizationComponent *)department_composite_constructor("後勤組");

   OrganizationComponent *leaf1 = (OrganizationComponent *)employee_leaf_constructor("唐三藏");
   OrganizationComponent *leaf2 = (OrganizationComponent *)employee_leaf_constructor("孫悟空");
   OrganizationComponent *leaf3 = (OrganizationComponent *)employee_leaf_constructor("豬悟能");
   OrganizationComponent *leaf4 = (OrganizationComponent *)employee_leaf_constructor("沙悟凈");

   com->add(com, com1);
   com->add(com, com2);
   com->add(com, com3);

   // leaf1屬於com1
   com1->add(com1, leaf1);
   // leaf2, leaf3屬於com2
   com2->add(com2, leaf2);
   com2->add(com2, leaf3);

   // 添加再刪除
   DepartmentComposite *dept1 = department_composite_constructor("小分隊");
   com2->add(com2, (OrganizationComponent *)dept1);
   EmployeeLeaf *tmp1 = employee_leaf_constructor("臨時工");
   dept1->add((OrganizationComponent *)dept1, (OrganizationComponent *)tmp1);
   dept1->remove((OrganizationComponent *)dept1, (OrganizationComponent *)tmp1);

   // leaf4屬於com3
   com3->add(com3, leaf4);

   // 執行全部節點動作
   com->operation(com);

   // 獲取某個節點
   OrganizationComponent *dept2 = com->get_child(com, 1);
   EmployeeLeaf *employee_child1 = (EmployeeLeaf *)dept2->get_child(dept2, 0);
   printf("\r\n [employee->name=%s]", employee_child1->name);

 

更多語言版本

不同語言實現設計模式源碼,請查看:https://github.com/microwind/design-pattern


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在接入華為運動健康服務的過程中你是否遇到過許可權申請有困難、功能不會用的情況? 本期超強精華帖,一帖彙總集成華為運動健康服務你可能需要的各類乾貨,還不趕緊收藏起來!開發有困難,隨時可查閱~ 如果你有感興趣或想進一步瞭解的內容,歡迎進行留言,或查看華為運動健康文檔獲取更多詳情! 許可權申請篇 在申請運動健 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 打開游戲界面,看到一個畫面簡潔、卻又富有挑戰性的游戲。屏幕上,有一個白色的矩形框,裡面不斷下落著各種單詞,而我需要迅速地輸入這些單詞。如果我輸入的單詞與屏幕上的單詞匹配,那麼我就可以獲得得分;如果我輸入的單詞錯誤或者時間過長,那麼我就會輸 ...
  • 本博文介紹CSS中常用的文本屬性,包括文本顏色、文本對齊、裝飾文本、文本縮進和行間距。 屬性 說明 屬性值 color 文本顏色 顏色(如red、green)#十六進位(如#ff0000) rgb代碼(如rgb(255,0,0)) text-align 文本對齊 left(預設值,左對齊) righ ...
  • 當我們需要執行動畫或其他高性能操作時,常常會遇到以下問題: - 任務的執行頻率過高,對 CPU 和記憶體造成了大量的壓力。- 任務的優先順序較高,導致其他任務無法及時得到處理。 為瞭解決這些問題,JavaScript 提供了兩個調度 API:requestAnimationFrame 和 request ...
  • 本博文介紹了CSS3中新增的選擇器,包括屬性選擇器、兩類結構偽類選擇器和偽元素選擇器,並對兩類結構偽類選擇器進行了比較。 ...
  • <div class="layui-row layui-col-space15" id="app"></div> 定義vueApp: let vueApp require(['vue'],function(Vue) { vueApp=new Vue({ el: "#app", data: { whe ...
  • 作者:京東科技 孫凱 一、前言 對前端開發者來說,Vite 應該不算陌生了,它是一款基於 nobundle 和 bundleless 思想誕生的前端開發與構建工具,官網對它的概括和期待只有一句話:“下一代的前端工具鏈”。 Vite 最早的版本由尤雨溪發佈於3年前,經歷了3年多的發展,Vite 也已逐 ...
  • 我們只會在 Observer 類 和 defineReactive 函數中實例化 dep。在 getter 方法中依賴收集,在 setter 方法中派發更新通知 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...