物件導向設計模式是軟體開發中的重要概念之一,而Composite(組合)模式是其中一種常用的設計模式之一。本文將探討Composite模式的實現與解說,以及其使用場景、UML圖示、設計優缺點,最後將深入研究Composite模式的實際實現,包括安全模式和透明模式的討論
透過本文的學習,您將更加深入了解Composite模式的運作原理和應用場景,有助於您在軟體開發中更加靈活地應用設計模式
可以把一組相似的物件,看做一個物件,就像是 數據結構 Tree,可以簡單把樹分成樹枝、葉,不斷的重複以組成一個樹(如果對這句話不清楚的話,可以先看以下的解說)
Composite 使用場景
- 使用者對於單個物件 & 組合物件的使用具有 一致性
- 從一個整體中能夠 獨立出部分模組 的功能或場景,使用上又有關聯性質 基本上有樹狀結構,這個設計模式都可以使用
Composite UML
- Composite 設計模式的 UML 角色介紹
類 | 功能 |
---|---|
Component(成分) | 末端節點與樹枝節點的共通點 |
Composite(合成) | 組裝模式的核心類,1. 繼承 Component 並且 2.內部聚集 Component 類 |
Left | 其下不會再有子節點 |
Composite 設計優缺點
- 優點 :
- 高層模組可以一致的==使用同一個組合結構或單一物件,實做部分讓低層模組做
- 清楚定義分層重複的物件,它讓高層模組忽略了層次的差異,方便對整個層次結構進行況控制
- 高層模組可以一致的==使用同一個組合結構或單一物件,實做部分讓低層模組做
- 缺點 :
在新增物件時 不好對枝幹中的類型進行限制 (如果要限制加入的物件類型),大多數情況下它們都來自相同的抽象層,此時必須進行類行檢查,過程較複雜
Composite 實現
Composite 實現有分為兩種方式,1. 安全模式、2. 透明模式,兩者各有優缺長短
Composite 標準:安全模式
A. Component
類:樹枝、根的共通點
abstract class Component constructor(val name: String) {
abstract fun printInfo()
}
B. Left
類:最基礎沒有子類的葉節點
class Department constructor(name : String) : Component(name) {
override fun printInfo() {
println("The department name is $name")
}
}
C. Composite
類:組合模式的核心,繼承 Component,並聚合 Component 抽象作為其頁節點
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()
}
● 使用 Composite 設計的範例:
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()
}
在使用的過程中,我們可以很清楚地發現,在添加子類時,必須使用實體實作類別的
addDepartment
方法,這就 不符合依賴倒置 的原則
Composite 變化:透明模式
● 從上面的安全模式下,我們可以發現「使用者必須取得 Composite 實作類」才能操作,但這就不符合依賴倒置規則;為了要符合依賴倒置的原則,我們可以把實現抽象化到 Component
層
● 透明的關鍵點在於,使用者在使用時不會再區分「根、葉」節點
可以從抽象類透明的看到所有的節點有的功能(但是實做上由節點決定如何處理)如果使用這種方式,建議使用「契約式」設計,將先驗條件定義在註解中(像是可能會拋出甚麽異常之類的)
● 抽象化到
Component
層 副作用A. 其實就是要注意到,抽象化過多會導致其他類必須處理(實作、繼承)不必要的功能,會造成類的膨脹
這要根據業務需求去衡量
B. 處理不當會造成 Runtime 時出錯
A. Component
類:修改該類,讓它先告所有方法,對外暴露(透明化)所有方法
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>
}
B. Left
類:對於不需要的方法拋出 UnsupportedOperationException
異常
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()
}
}
C. Composite
類:該類的實現基本沒變
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()
}
● 使用 Composite 設計的範例:
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()
}
更多的物件導向設計
物件導向的設計基礎如下,如果是初學者或是不熟悉的各位,建議可以從這些基礎開始認識,打好基底才能走個更穩(在學習的時候也需要不斷回頭看)!
創建模式 Creation Patterns
● 創建模式 PK
● 創建模式 - Creation Patterns
:
創建模式用於「物件的創建」,它關注於如何更靈活、更有效地創建物件。這些模式可以隱藏創建物件的細節,並提供創建物件的機制,例如單例模式、工廠模式… 等等,詳細解說請點擊以下連結
● Singleton 單例模式 | 解說實現 | Android Framework Context Service
● Abstract Factory 設計模式 | 實現解說 | Android MediaPlayer
● Factory 工廠方法模式 | 解說實現 | Java 集合設計
● Builder 建構者模式 | 實現與解說 | Android Framwrok Dialog 視窗
● Clone 原型模式 | 解說實現 | Android Framework Intent
行為模式 Behavioral Patterns
● 行為模式 PK
● 行為模式 - Behavioral Patterns
:
行為模式關注物件之間的「通信」和「職責分配」。它們描述了一系列物件如何協作,以完成特定任務。這些模式專注於改進物件之間的通信,從而提高系統的靈活性。例如,策略模式、觀察者模式… 等等,詳細解說請點擊以下連結
● Stragety 策略模式 | 解說實現 | Android Framework 動畫
● Interpreter 解譯器模式 | 解說實現 | Android Framework PackageManagerService
● Chain 責任鏈模式 | 解說實現 | Android Framework View 事件傳遞
● Specification 規格模式 | 解說實現 | Query 語句實做
● Command 命令、Servant 雇工模式 | 實現與解說 | 物件導向設計
● Memo 備忘錄模式 | 實現與解說 | Android Framwrok Activity 保存
● Visitor 設計模式 | 實現與解說 | 物件導向設計
● Template 設計模式 | 實現與解說 | 物件導向設計
● Mediator 模式設計 | 實現與解說 | 物件導向設計
● Composite 組合模式 | 實現與解說 | 物件導向設計
● Observer 觀察者模式 | JDK Observer | Android Framework Listview
結構模式 Structural Patterns
● 結構模式 PK
● 結構模式 - Structural Patterns
:
結構模式專注於「物件之間的組成」,以形成更大的結構。這些模式可以幫助你確保當系統進行擴展或修改時,不會破壞其整體結構。例如,外觀模式、代理模式… 等等,詳細解說請點擊以下連結
● Decorate 裝飾模式 | 解說實現 | 物件導向設計
● Iterator 迭代設計 | 解說實現 | 物件導向設計