--- 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
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up