當要建立大量相似物件時,把可共享的不可變狀態(intrinsic)抽出來共用,把每個情境才不同的狀態(extrinsic)由外部傳入,藉此大幅省記憶體。
🧠 When to Use
TIP
訊號:你要產生海量物件(文字每個字元、地圖每塊磚、粒子系統),而它們大部分欄位其實長得一樣。
- 物件數量龐大,記憶體成為瓶頸。
- 物件狀態可清楚拆成共享的 intrinsic 與情境相依的 extrinsic。
- 移除 extrinsic 後,剩下的 intrinsic 群組數量很少、可被大量重用。
⚖️ Structure & Variants
經典版 — 共享 intrinsic、外傳 extrinsic
// Flyweight:只存可共享、不可變的 intrinsic 狀態(字形外觀)
class Glyph(val char: Char, val font: Font)
// Factory:同 key 回傳同一個共享實例
object GlyphFactory {
private val pool = HashMap<Pair<Char, Font>, Glyph>()
fun get(c: Char, f: Font): Glyph = pool.getOrPut(c to f) { Glyph(c, f) }
}
// extrinsic 狀態(位置)由呼叫端傳入,不存進 Glyph
fun drawAt(g: Glyph, x: Int, y: Int) { /* render g at (x, y) */ }
val a = GlyphFactory.get('a', Font.SERIF) // 整份文件的所有 'a' 共用同一個- 一份十萬字的文件,只需數十個 Glyph 實例。
intrinsic vs extrinsic 的界線
- Intrinsic:與情境無關、可共享、必須不可變(字元、字型、磚塊貼圖)。
- Extrinsic:每次使用才決定、由外部傳入(座標、旋轉、當前狀態)。
- 界線畫錯(把該外傳的塞進共享物件)會破壞共享、甚至引發競態。
和 Singleton / Composite 的關係
⚠️ Misuse & Anti-patterns
- 物件量不大卻硬上:享元的間接與 factory 複雜度,只在海量物件時才划算。
- 共享物件可變:intrinsic 一旦可變,一處修改會污染所有共享者,是嚴重 bug 來源。
- extrinsic 算不清:若大部分狀態都是情境相依的,根本沒得共享,模式失效。
🔑 Takeaways
- 解決的是「海量相似物件吃記憶體」:共享 intrinsic、外傳 extrinsic。
- 成立前提:intrinsic 不可變且 intrinsic 群組數遠小於物件總數。
- 由 factory 池管理共享實例;和 Singleton 的差別是「一群有限」而非「唯一」。
No notes yet — jot your takeaways or Q&A here.