您可於此 design_pattern repo 下載 Design Pattern 系列程式碼。
需求
假設我們正在開發一個森林場景的渲染系統,該系統需要顯示數百棵甚至數千棵樹木。
每棵樹包含兩類資料:
- 內部狀態 (Intrinsic State):不隨環境改變的資料,例如樹的種類、顏色、紋理等。
- 外部狀態 (Extrinsic State):因環境而異的資料,例如樹的座標 (x, y)。
如果為每棵樹都建立完整的物件,將導致記憶體消耗過大。因此,我們需要一種共享內部狀態的方式來優化記
物件導向分析 (OOA)
理解需求後,讓我們來快速實作物件導向分析吧!
察覺 Forces
在設計階段,我們注意到以下設計難題:
- 大量重複資料:每棵樹都包含相同的種類、顏色和紋理資料。
- 性能問題:對於數千棵樹的場景渲染,過多的物件會導致記憶體不足或性能瓶頸。
- 共享與獨立的平衡:如何在共享資料的同時,保留每棵樹的獨立外部狀態。
為解決這些問題,我們採用了享元模式。
套用 Flyweight Pattern (Solution) 得到新的 Context (Resulting Context)
做完 OOA,察覺 Forces,看清楚整個 Context 後,就可以來套用 Flyweight Pattern 解決這個問題。
先來看一下 flyweight Pattern 的 UML:
- Flyweight (享元介面):定義共享物件的操作。
- ConcreteFlyweight (具體享元類別):實作共享物件的功能,儲存可以共享的狀態。
- FlyweightFactory (享元工廠):用於創建和管理共享物件,確保相同的物件只創建一次。
- Client (客戶端):使用享元物件,並管理不能共享的狀態。
將 flyweight Pattern 套用到我們的應用吧
物件導向程式設計 (OOP)
[FFlyweight: Tree & TreeType (樹類別)]
class Tree(
private val x: Int,
private val y: Int,
private val type: TreeType
) {
fun draw() {
type.draw(x, y)
}
}
class TreeType(
val name: String,
val color: String,
val texture: String
) {
fun draw(x: Int, y: Int) {
println("Drawing tree: $name, color: $color, texture: $texture at ($x, $y)")
}
}
[FlyweightFactory: TreeFactory (樹工廠類別)]
object TreeFactory {
private val treeTypes = mutableMapOf<String, TreeType>()
fun getTreeType(name: String, color: String, texture: String): TreeType {
return treeTypes.computeIfAbsent(name) {
println("Creating new TreeType: $name")
TreeType(name, color, texture)
}
}
}
[Client: Forest (森林類別)]
class Forest {
private val trees = mutableListOf<Tree>()
fun plantTree(x: Int, y: Int, name: String, color: String, texture: String) {
val treeType = TreeFactory.getTreeType(name, color, texture)
val tree = Tree(x, y, treeType)
trees.add(tree)
}
fun draw() {
for (tree in trees) {
tree.draw()
}
}
}
[Main Function]
fun main() {
val forest = Forest()
// Planting trees in the forest
forest.plantTree(10, 20, "Oak", "Green", "Rough")
forest.plantTree(15, 25, "Pine", "Dark Green", "Smooth")
forest.plantTree(10, 20, "Oak", "Green", "Rough") // Reuses the same TreeType as the first Oak
// Draw all trees
forest.draw()
}
[Output]
Creating new TreeType: Oak
Creating new TreeType: Pine
Drawing tree: Oak, color: Green, texture: Rough at (10, 20)
Drawing tree: Pine, color: Dark Green, texture: Smooth at (15, 25)
Drawing tree: Oak, color: Green, texture: Rough at (10, 20)
結論
享元模式通過共享技術,有效降低了系統的記憶體使用量,提升了效能。它特別適用於需要大量重複物件的情境,例如文字編輯器、遊戲開發等場景。然而,在使用時需要小心區分內部與外部狀態,以確保系統設計的正確性與靈活性。
Leave a comment