--- 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 | 其下不會再有子節點 | >  ### 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` 方法,這就 **不符合依賴倒置 的原則** ::: >  ### Composite 變化 - 透明模式 * 從上面的安全模式下,我們可以發現「**使用者必須取得 Composite 實作類**」才能操作,但這就不符合依賴倒置規則; 為了要符合依賴倒置的原則,我們可以把實現抽象化到 `Component` 層 :::info * 透明的關鍵點在於,使用者在使用時不會再區分「根、葉」節點 可以從抽象類透明的看到所有的節點有的功能(但是實做上由節點決定如何處理) > 如果使用這種方式,建議使用「契約式」設計,將先驗條件定義在註解中(像是可能會拋出甚麽異常之類的) ::: :::warning * 抽象化到 `Component` 層 副作用 1. 其實就是要注意到,抽象化過多會導致其他類必須處理(實作、繼承)不必要的功能,會造成類的膨脹 > 這要根據業務需求去衡量 2. 處理不當會造成 Runtime 時出錯 ::: >  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() } ``` >  ## 更多的物件導向設計 物件導向的設計基礎如下,如果是初學者或是不熟悉的各位,建議可以從這些基礎開始認識,打好基底才能走個更穩(在學習的時候也需要不斷回頭看)! :::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 設計模式` `基礎進階`
×
Sign in
Email
Password
Forgot password
or
Sign in via Google
Sign in via Facebook
Sign in via X(Twitter)
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
Continue with a different method
New to HackMD?
Sign up
By signing in, you agree to our
terms of service
.