---
# System prepended metadata

title: Composite 組合模式
tags: [基礎進階, Java 設計模式]

---

---
title: 'Composite 組合模式'
disqus: kyleAlien
---

Composite 組合模式
===

## Overview of Content

如有引用參考請詳註出處，感謝 :smile:

>可以**把一組相似的物件，看做一個物件**，就像是 [**數據結構 Tree**](https://hackmd.io/ZQOBnRnlTM-0XEwkJfzPqQ)，可以**簡單把樹分成樹枝、葉**，不斷的重複以組成一個樹

:::success
* 如果喜歡讀更好看一點的網頁版本，可以到我新做的網站 [**DevTech Ascendancy Hub**](https://devtechascendancy.com/)
    
    本篇文章對應的是 [**Composite 組合模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_programming_composite/)
:::

[TOC]

## 使用場景
* 使用者**對於單個物件 & 組合物件的使用具有==一致性==**

* 從一個**整體中能夠==獨立出部分模組==** 的功能或場景，使用上**又有關聯性質**

    > 基本上有樹狀結構，這個設計模式都可以使用

* 組合模式常與策略、命令模式一起使用；組合是基於多態，並對使用方提供「防變異性（`GRASP`）」，使其不會因為相關的物件細節而受到影響（像是具體子類還是組合中間類別）；不過相對的也會提高設計的複雜度

### Composite UML

* 角色介紹

    | 類 | 功能 |
    | - | - |
    | Component（成分） | 末端節點與樹枝節點的共通點 |
    | Composite（合成） | **組裝模式的核心類**，^1.^ 繼承 Component 並且 ^2.^內部聚集 Component 類 |
    | Left | 其下不會再有子節點 |

    > ![](https://i.imgur.com/y49YnGu.png)

### Composite 設計 - 優缺點

* 優點 : 
    1. 高層模組可以==一致的==使用同一個組合結構或單一物件，實做部分讓低層模組做
    
    2. 清楚定義**分層重複的物件**，它讓高層模組忽略了層次的差異，**方便對整個層次結構進行況控制**

* 缺點 : 
    
    在新增物件時 **不好對枝幹中的類型進行限制** (如果要限制加入的物件類型)，大多數情況下它們都來自相同的抽象層，此時必須進行類行檢查，過程較複雜



## Composite 實現

Composite 實現有分為兩種方式，^1.^ 安全模式、^2.^ 透明模式，兩者各有優缺長短

### Composite 標準 - 安全模式

1. **`Component` 類**：樹枝、根的共通點

    ```kotlin=
    abstract class Component constructor(val name: String) {

        abstract fun printInfo()

    }
    ```
    
2. **`Left` 類**：最基礎沒有子類的葉節點

    ```kotlin=
    class Department constructor(name : String) : Component(name) {

        override fun printInfo() {
            println("The department name is $name")
        }

    }
    ```
    
3. **`Composite` 類**：組合模式的核心，繼承 Component，並聚合 Component 抽象作為其頁節點

    ```kotlin=
    class Company constructor(name: String) : Component(name) {

        private val childList = mutableListOf<Component>()

        override fun printInfo() {
            childList.iterator().apply {
                while (hasNext()) {
                    next().printInfo()
                }
            }
        }

        fun addDepartment(child: Component) {
            childList.contains(child).let {  contains ->
                if (contains) {
                    return
                }

                childList.add(child)
            }
        }

        fun removeDepartment(child: Component) {
            childList.remove(child)
        }

        fun getDepartmentList() : Iterator<Component> = childList.iterator()


    }
    ```

* **使用範例**：

    ```kotlin=
    fun main() {
        val company = Company("AUSU 總公司")

        // 新建一個根結點 Company (Root)，多個子結點
        // 可以簡單看出其實就分為兩個部分，終端節點 & 分枝節點
        Department("總-公關部門").also {
            
            // 不符合依賴倒置
            company.addDepartment(it)
        }
        Department("總-業務部門").also {
            company.addDepartment(it)
        }
        Department("總-研發部門").also {
            company.addDepartment(it)
        }
        Department("總-財務部門").also {
            company.addDepartment(it)
        }

        Company("AUSU 子公司").also {
            company.addDepartment(it)
        }.let { child ->
            Department("子-公關部門").also {
                child.addDepartment(it)
            }
            Department("子-業務部門").also {
                child.addDepartment(it)
            }
        }

        company.printInfo()
    }
    ```
    :::warning
    在使用的過程中，我們可以很清楚地發現，在添加子類時，必須使用實體實作類別的 `addDepartment` 方法，這就 **不符合依賴倒置 的原則**
    :::
    
    > ![](https://i.imgur.com/8Jhoq7K.png)

### Composite 變化 - 透明模式

* 從上面的安全模式下，我們可以發現「**使用者必須取得 Composite 實作類**」才能操作，但這就不符合依賴倒置規則；

    為了要符合依賴倒置的原則，我們可以把實現抽象化到 `Component` 層
    
    :::info
    * 透明的關鍵點在於，使用者在使用時不會再區分「根、葉」節點
        
        可以從抽象類透明的看到所有的節點有的功能（但是實做上由節點決定如何處理）
        
        > 如果使用這種方式，建議使用「契約式」設計，將先驗條件定義在註解中（像是可能會拋出甚麽異常之類的）
    :::
    
    :::warning
    * 抽象化到 `Component` 層 副作用

        1. 其實就是要注意到，抽象化過多會導致其他類必須處理（實作、繼承）不必要的功能，會造成類的膨脹
        
            > 這要根據業務需求去衡量

        2. 處理不當會造成 Runtime 時出錯
    :::
    
    > ![](https://i.imgur.com/VdwQTki.png)


1. **`Component` 類**：修改該類，讓它先告所有方法，對外暴露（透明化）所有方法

    ```kotlin=
    abstract class TransparentComponent 
            val name: String) {
                
        protected val childList = mutableListOf<TransparentComponent>()

        abstract fun printInfo()

        abstract fun addDepartment(child: TransparentComponent)

        abstract fun removeDepartment(child: TransparentComponent)

        abstract fun getDepartmentList() : Iterator<TransparentComponent>

    }
    ```
    
2. **`Left` 類**：對於不需要的方法拋出 `UnsupportedOperationException` 異常

    ```kotlin=
    class Department2 constructor(name: String) : TransparentComponent(name) {
        override fun printInfo() {
            println("The department name is $name")
        }

        override fun addDepartment(child: TransparentComponent) {
            throw UnsupportedOperationException()
        }

        override fun removeDepartment(child: TransparentComponent) {
            throw UnsupportedOperationException()
        }

        override fun getDepartmentList(): Iterator<TransparentComponent> {
            throw UnsupportedOperationException()
        }

    }
    ```
    
3. **`Composite` 類**：該類的實現基本沒變

    ```kotlin=
    class Company2 constructor(name: String) : TransparentComponent(name) {
        override fun printInfo() {
            childList.iterator().apply {
                while (hasNext()) {
                    next().printInfo()
                }
            }
        }

        override fun addDepartment(child: TransparentComponent) {
            childList.contains(child).let {  contains ->
                if (contains) {
                    return
                }

                childList.add(child)
            }
        }

        override fun removeDepartment(child: TransparentComponent) {
            childList.remove(child)
        }

        override fun getDepartmentList(): Iterator<TransparentComponent> = childList.iterator()

    }
    ```
    
* **使用範例**：符合依賴倒置就可以使用依賴抽象

    ```kotlin=
    fun main() {
        // 依賴抽象
        val company : TransparentComponent = Company2("AUSU 總公司")

        Department2("總-公關部門").also {
            company.addDepartment(it)
        }
        Department2("總-業務部門").also {
            company.addDepartment(it)
        }
        Department2("總-研發部門").also {
            company.addDepartment(it)
        }
        Department2("總-財務部門").also {
            company.addDepartment(it)
        }

        Company2("AUSU 子公司").also {
            company.addDepartment(it)
        }.let { child ->
            Department2("子-公關部門").also {
                child.addDepartment(it)
            }
            Department2("子-業務部門").also {
                child.addDepartment(it)
            }
        }

        company.printInfo()
    }
    ```
    > ![](https://i.imgur.com/qfOSHZO.png)
    
## 更多的物件導向設計

物件導向的設計基礎如下，如果是初學者或是不熟悉的各位，建議可以從這些基礎開始認識，打好基底才能走個更穩（在學習的時候也需要不斷回頭看）！

:::info
* [**設計建模 2 大概念- UML 分類、使用**](https://devtechascendancy.com/introduction-to-uml-and-diagrams/)

* [**物件導向設計原則 – 6 大原則(一)**](https://devtechascendancy.com/object-oriented-design-principles_1/)

* [**物件導向設計原則 – 6 大原則(二)**](https://devtechascendancy.com/object-oriented-design-principles_2/)
:::


### 創建模式 - Creation Patterns

* [**創建模式 PK**](https://devtechascendancy.com/pk-design-patterns-factory-builder-best/)

* **創建模式 - `Creation Patterns`**：
    
    創建模式用於「**物件的創建**」，它關注於如何更靈活、更有效地創建對象。這些模式可以隱藏創建對象的細節，並提供創建對象的機制，例如單例模式、工廠模式… 等等，詳細解說請點擊以下連結

    :::success
    
    * [**Singleton 單例模式 | 解說實現 | Android Framework Context Service**](https://devtechascendancy.com/object-oriented_design_singleton/)

    * [**Abstract Factory 設計模式 | 實現解說 | Android MediaPlayer**](https://devtechascendancy.com/object-oriented_design_abstract-factory/)

    * [**Factory 工廠方法模式 | 解說實現 | Java 集合設計**](https://devtechascendancy.com/object-oriented_design_factory_framework/)

    * [**Builder 建構者模式 | 實現與解說 | Android Framwrok Dialog 視窗**](https://devtechascendancy.com/object-oriented_design_builder_dialog/)
    
    * [**Clone 原型模式 | 解說實現 | Android Framework Intent**](https://devtechascendancy.com/object-oriented_design_clone_framework/)

    * [**Object Pool 設計模式 | 實現與解說 | 利用 JVM**](https://devtechascendancy.com/object-oriented_design_object-pool/)

    * [**Flyweight 享元模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_flyweight/)
    :::


### 行為模式 - Behavioral Patterns

* [**行為模式 PK**](https://devtechascendancy.com/pk-design-patterns-cmd-strat-state-obs-chain/)

* **行為模式 - `Behavioral Patterns`**：
    
    行為模式關注物件之間的「**通信**」和「**職責分配**」。它們描述了一系列對象如何協作，以完成特定任務。這些模式專注於改進物件之間的通信，從而提高系統的靈活性。例如，策略模式、觀察者模式… 等等，詳細解說請點擊以下連結

    :::warning
    * [**Stragety 策略模式 | 解說實現 | Android Framework 動畫**](https://devtechascendancy.com/object-oriented_design_stragety_framework/)

    * [**Interpreter 解譯器模式 | 解說實現 | Android Framework PackageManagerService**](https://devtechascendancy.com/object-oriented_design_interpreter_framework/)

    * [**Chain 責任鏈模式 | 解說實現 | Android Framework View 事件傳遞**](https://devtechascendancy.com/object-oriented_design_chain_framework/)

    * [**State 狀態模式 | 實現解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_state/)

    * [**Specification 規格模式 | 解說實現 | Query 語句實做**](https://devtechascendancy.com/object-oriented_design_specification-query/)

    * [**Command 命令、Servant 雇工模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_command_servant/)

    * [**Memo 備忘錄模式 | 實現與解說 | Android Framwrok Activity 保存**](https://devtechascendancy.com/object-oriented_design_memo_framework/)

    * [**Visitor 設計模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_visitor_dispatch/)

    * [**Template 設計模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_template/)

    * [**Mediator 模式設計 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_programming_mediator/)

    * [**Composite 組合模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_programming_composite/)
    :::


### 結構模式 - Structural Patterns

* [**結構模式 PK**](https://devtechascendancy.com/pk-design-patterns-proxy-decorate-adapter/)

* **結構模式 - `Structural Patterns`**：
    
    結構模式專注於「物件之間的組成」，以形成更大的結構。這些模式可以幫助你確保當系統進行擴展或修改時，不會破壞其整體結構。例如，外觀模式、代理模式… 等等，詳細解說請點擊以下連結

    :::danger
    * [**Bridge 橋接模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_bridge/)

    * [**Decorate 裝飾模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_decorate/)

    * [**Proxy 代理模式 | 解說實現 | 分析動態代理**](https://devtechascendancy.com/object-oriented_design_proxy_dynamic-proxy/)

    * [**Iterator 迭代設計 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_iterator/)

    * [**Facade 外觀、門面模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_facade/)

    * [**Adapter 設計模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_adapter/)
    :::



## Appendix & FAQ
:::info

:::

###### tags: `Java 設計模式` `基礎進階`