🎯 什麼情境該想到我
當你「程式需要產生極大量、細粒度、彼此又很相似的物件(文件裡的每個字元、地圖上的每棵樹、遊戲裡每一顆子彈),一個一個都建成完整物件會把記憶體吃光」的時候。
⚙️ 怎麼用(步驟 / 公式)
意圖:運用共享來有效率地支援大量細粒度的物件。關鍵是把物件狀態拆成可共享的「內在狀態」與不可共享的「外在狀態」。
主要參與者 / 結構:
- Flyweight(享元):宣告可接受並作用於外在狀態的介面。
- ConcreteFlyweight(具體享元):可共享的物件,只儲存內在狀態(與情境無關、可跨處重複使用,如字元的字形資料)。
- FlyweightFactory(享元工廠):建立並管理享元;有人要時先查快取池,已存在就回傳共享的那一個,否則才新建。
- Client:保存或計算外在狀態(與情境有關、如座標/顏色),在呼叫享元操作時把它傳入。
做法要點:
- 分析物件狀態,切出「內在(可共享)」與「外在(每次不同)」兩部分。
- 內在狀態放進享元物件;外在狀態移出,改由客戶端持有並在呼叫時傳入。
- 一律透過工廠取得享元,讓相同內在狀態的物件被重複共享。
🧪 我實際套用的紀錄
- (待填)
⚠️ 注意 / 什麼時候不適用
- 增加複雜度:要正確區分內在/外在狀態,並把外在狀態的管理責任丟給客戶端。
- 用計算換記憶體:外在狀態每次傳入或重算可能增加執行時間。
- 若物件數量本來就不多、或狀態幾乎都不能共享,省下的記憶體不值得這份複雜度。
- 享元通常要是不可變的(immutable),共享的東西被改到會全部一起壞。
🔗 相關工具
- 組合模式 Composite(常搭配:把組合樹中大量重複的葉子節點做成共享享元)
- 代理模式 Proxy(都在物件建立/取得上做手腳,代理是控制存取、享元是共享實例以省資源)
- 工具-封裝變化點(把「哪些狀態能共享」的決策封裝在享元與工廠內)
- 設計模式