結構型模式:組合模式

来源:https://www.cnblogs.com/liebrother/archive/2019/04/24/10760440.html
-Advertisement-
Play Games

文章首發: "結構型模式:組合模式" 七大結構型模式之三:組合模式。 簡介 姓名 :組合模式 英文名 :Composite Pattern 價值觀 :專門解決各種樹形疑難雜症 個人介紹 : Compose objects into tree structures to represent part ...


文章首發:
結構型模式:組合模式

夜晚

七大結構型模式之三:組合模式。

簡介

姓名 :組合模式

英文名 :Composite Pattern

價值觀 :專門解決各種樹形疑難雜症

個人介紹
Compose objects into tree structures to represent part-whole hierarchies.Composite lets clients treat individual objects and compositions of objects uniformly.
將對象組合成樹形結構以表示“部分-整體”的層次結構,使得用戶對單個對象和組合對象的使用具有一致性。
(來自《設計模式之禪》)

你要的故事

今天咱們再講講咱們程式猿的組織架構。技術類的組織架構比較單一,基本上都是這樣:經理--->組長--->工程師,如下圖所示。

IT組織架構

各個公司的 title 可能不太一樣,但是基本是差不多這種架構,按職業發展,從入職到能獨立開發需求便為工程師,從獨立開發需求到能帶小團隊開發便為組長,從帶小團隊開發到能帶幾個團隊一起協作開發便為經理。

假設目前有一家公司,技術部就 4 個人,大熊擔任經理,中熊擔任組長,小熊1和小熊2擔任工程師。下麵的代碼都圍繞這個假設編寫。

非組合模式

我們先來一個非正常的實現方案:從組織架構里,有 3 個角色,分別是經理、組長、工程師,那麼我們就按角色去實現一番。

Manager 為經理類,經理下有多個組長 leaders。

/**
 * 經理
 */
class Manager {

    private String name;
    private List<Leader> leaders;

    public Manager(String name) {
        this.name = name;
        this.leaders = new LinkedList<>();
    }

    public void add(Leader leader) {
        this.leaders.add(leader);
    }

    public void remove(Leader leader) {
        this.leaders.remove(leader);
    }

    public void display(int index) {
        for (int i = 0; i < index; i++) {
            System.out.print("----");
        }
        System.out.println("經理:" + this.name);
        leaders.forEach(leader -> {
            leader.display(index+1);
        });
    }

}

Leader 為組長類,組長下有多個工程師 engineers。

/**
 * 組長
 */
class Leader {

    private String name;
    private List<Engineer> engineers;

    public Leader(String name) {
        this.name = name;
        this.engineers = new LinkedList<>();
    }

    public void add(Engineer engineer) {
        this.engineers.add(engineer);
    }

    public void remove(Engineer engineer) {
        this.engineers.remove(engineer);
    }

    public void display(int index) {
        for (int i = 0; i < index; i++) {
            System.out.print("----");
        }
        System.out.println("組長:" + this.name);
        engineers.forEach(engineer -> {
            engineer.display(index + 1);
        });
    }
}

Engineer 為工程師類,工程師沒有下屬。

/**
 * 工程師
 */
class Engineer {

    private String name;

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

    public void display(int index) {
        for (int i = 0; i < index; i++) {
            System.out.print("----");
        }
        System.out.println("工程師:" + this.name);
    }

}

測試代碼

public class NoCompositeTest {

    public static void main(String[] args) {
        Manager manager = new Manager("大熊");
        Leader leader = new Leader("中熊");
        Engineer engineer1= new Engineer("小熊1");
        Engineer engineer2 = new Engineer("小熊2");

        manager.add(leader);
        leader.add(engineer1);
        leader.add(engineer2);

        manager.display(0);
    }

}

列印結果:
經理:大熊
----組長:中熊
--------工程師:小熊1
--------工程師:小熊2

這份代碼看完之後,有什麼想法?是不是感覺代碼有點冗餘?經理和組長的代碼幾乎一致,而工程師類和經理類、組長類也有共同點,唯一的區別就是工程師沒有下屬,因此沒有對下屬的增刪操作方法。

安全模式

通過上面一層思考,這 3 個角色有相通性,我們可以抽象出一個 Employee2 類,把 3 個角色共同的特性放到 Employee2 類中,經理和組長合併共用一個類,因為在這個例子里,這 2 個角色完全一樣的。下麵看代碼。

Employee2 抽象類,它有這 3 個角色共有的特性,名稱設置獲取以及顯示數據。

abstract class Employee2 {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public abstract void display(int index);

}

Leader2 領導類,把上面的經理類和組長類都合併到這個領導類,因為他們都是領導層。

class Leader2 extends Employee2 {

    private List<Employee2> employees;

    public Leader2(String name) {
        this.setName(name);
        this.employees = new ArrayList<>();
    }

    public void add(Employee2 employee) {
        this.employees.add(employee);
    }

    public void remove(Employee2 employee) {
        this.employees.remove(employee);
    }

    @Override
    public void display(int index) {
        for(int i = 0; i < index; i++) {
            System.out.print("----");
        }
        System.out.println("領導:" + this.getName());
        this.employees.forEach(employee -> {
            employee.display(index + 1);
        });
    }
}

Engineer2 工程師類,工程師類比較簡單,因為名稱設置獲取在抽象類 Employee2 有了,所以就只需實現顯示數據的功能。

class Engineer2 extends Employee2 {

    public Engineer2(String name) {
        this.setName(name);
    }

    @Override
    public void display(int index) {
        for(int i = 0; i < index; i++) {
            System.out.print("----");
        }
        System.out.println("工程師:" + this.getName());
    }
}

測試代碼

public class CompositeTest {

    public static void main(String[] args) {
        // 安全模式
        Leader2 leader1 = new Leader2("大熊");
        Leader2 leader2 = new Leader2("中熊");
        Engineer2 engineer1 = new Engineer2("小熊1");
        Engineer2 engineer2 = new Engineer2("小熊2");

        leader1.add(leader2);
        leader2.add(engineer1);
        leader2.add(engineer2);

        leader1.display(0);
    }

}

列印結果:
領導:大熊
----領導:中熊
--------工程師:小熊1
--------工程師:小熊2

看下運行結果和上面是一致的,這份代碼比第一份代碼有更好的封裝性,也更符合面向對象的編程方式,經理和組長被合併成 Leader2,也就是咱們今天講的組合模式,Leader2 為組合對象。上面講的是安全模式,安全模式指的是抽象類 Employee2 只提供了 3 個角色中共有的特性,安全是相對透明模式所說的,因為這裡領導類 Leader2 和工程師類 Engineer2 都只提供了自己能提供的方法,Engineer2 不會有多餘的方法,而透明模式則不是。下麵講講透明模式。

透明模式

透明模式把組合對象(即領導類)使用的方法放到抽象類中,而因為工程師沒有下屬,則不具體實現對應的方法。代碼如下。

Employee3 抽象類,將組合對象的屬性 employees 和 方法 add()remove() 都放到這個類裡面。

abstract class Employee3 {

    private String name;
    private List<Employee3> employees;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Employee3> getEmployees() {
        return employees;
    }

    public void setEmployees(List<Employee3> employees) {
        this.employees = employees;
    }

    public abstract void add(Employee3 employee);

    public abstract void remove(Employee3 employee);

    public abstract void display(int index);

}

Leader3 領導類,具體實現 Employee3 提供的所有方法。

class Leader3 extends Employee3 {

    public Leader3(String name) {
        this.setName(name);
        this.setEmployees(new ArrayList<>());
    }

    @Override
    public void add(Employee3 employee) {
        this.getEmployees().add(employee);
    }

    @Override
    public void remove(Employee3 employee) {
        this.getEmployees().remove(employee);
    }

    @Override
    public void display(int index) {
        for(int i = 0; i < index; i++) {
            System.out.print("----");
        }
        System.out.println("領導:" + this.getName());
        this.getEmployees().forEach(employee -> {
            employee.display(index + 1);
        });
    }
}

Engineer3 工程師類,只具體實現 Employee3 中的 display() 方法,add()remove() 方法不是工程師具備的,所以留空,不做具體實現。

class Engineer3 extends Employee3 {

    public Engineer3(String name) {
        this.setName(name);
    }

    @Override
    public void add(Employee3 employee) {
        // 沒有下屬
    }

    @Override
    public void remove(Employee3 employee) {
        // 沒有下屬
    }

    @Override
    public void display(int index) {
        for(int i = 0; i < index; i++) {
            System.out.print("----");
        }
        System.out.println("工程師:" + this.getName());
    }
}

測試代碼:

public class CompositeTest {

    public static void main(String[] args) {
        // 透明模式
        Leader3 leader3 = new Leader3("大熊");
        Leader3 leader31 = new Leader3("中熊");
        Engineer3 engineer31 = new Engineer3("小熊1");
        Engineer3 engineer32 = new Engineer3("小熊2");

        leader3.add(leader31);
        leader31.add(engineer31);
        leader31.add(engineer32);

        leader3.display(0);

    }

列印結果:
領導:大熊
----領導:中熊
--------工程師:小熊1
--------工程師:小熊2
}

安全模式把 3 個角色的共同點抽象到 Employee2 中,透明模式則把 3 個角色中的領導者(組合對象)的內容抽象到 Employee3 中。透明模式有些不好的地方在於工程師也有領導者的下屬對象和相應的方法,其實工程師並沒有這些功能。安全模式把領導者和工程師分開,每個對象都只提供自己具有的功能,這樣子在使用的時候也就更安全。

總結

我們根據 IT 組織架構,從簡單的每個角色對應一個類的實現,再到抽象出每個角色共同的功能、組合領導類的安全模式,接著再到抽象起來領導類(組合)所有功能的透明模式,分析了組合模式的完整過程,也講了安全模式和透明模式的差異。組合模式讓對象更加有層次,將對象的劃分更加清晰,特別是樹形結構的層次,利用組合模式會更加簡化。

推薦閱讀

結構型模式:橋接模式

結構型模式:適配器模式

行為型模式:解釋器模式

公眾號後臺回覆『大禮包』獲取 Java、Python、IOS 等教程
加個人微信備註『教程』獲取架構師、機器學習等教程

LieBrother


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

-Advertisement-
Play Games
更多相關文章
  • 今天練習一個小demo, 從本地讀取圖片, 然後實現類似淘寶放大鏡的效果, 再加兩個需求 1 .可以調節縮放比例,預設放大兩倍 2 . 圖片寬高自適應, 不固定寬高 話不多說先看效果: 原理:1, 右側放大區域的大小等於左側半透明滑塊大小乘以縮放倍數 2, 右側放大區域圖片的原始尺寸要和 左側圖片一 ...
  • <html><body> <h3>js控制文件上傳數量</h3> <form action="" enctype="multipart/form-data"> <input type="file" name="file" multiple="multiple" onchange="fileCount ...
  • 一、什麼是小程式? 基於微信的可以為用戶提供一些服務的web項目,利用微信提供的介面可以讓所有開發者使用到微信的原生能力,去完成一些之前做不到或者難以做到的事情。 二、小程式開發工具以及語言? 小程式需要用到微信提供的小程式開發工具,​小程式的主要開發語言是 JavaScript 。 三、小程式與普 ...
  • 項目中經常遇到要選擇城市。用到三級聯動的方式 微信小程式的 組件 是三級聯動的,但是無法自定義,這讓我們心痛不已,值得我們欣慰的 picker view 組件是可以自定義添加多個選項,但還是無法聯動。既然這樣那就自己寫一個聯動。 做到如下圖所示: 分為動態獲取地址 引用靜態文件獲取地址 <! mor ...
  • 轉自:https://www.cnblogs.com/kidsitcn/p/7182274.html 比例尺函數是這樣的javascript函數: 接收通常是數字,日期,類別等data輸入並且: 返回一個代表可視化元素的值,比如坐標,顏色,長度或者半徑等 比例尺通常用於變換(或者說映射)抽象的數據值 ...
  • 做法就是使用iframe標簽 1.text,pdf的文件預覽 <iframe class="filename" :src="文件的地址" width='100%' height='600' frameborder='1' ></iframe> 2.doc,xls,ppt等office的預覽 <ifr ...
  • 一、對象的擴展 1.1對象屬性名錶達式 ES6可以在JSON中使用[]包裹一個key的名字。此時這個key將用表達式作為屬性名(被當做變數求值),這個key值必須是字元串。 1.2 Object.assign()方法 該方法用於對象的合併,將源對象的所有可枚舉的屬性,複製到目標對象。 Object. ...
  • 譯者按: 為什麼偏要用 符號? 原文 : "JavaScript's new private class fields" 譯者 : "Fundebug" 本文采用意譯,版權歸原作者所有 "proposal class fields" 與 "proposal private methods" 定義了 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...