iT邦幫忙

2025 iThome 鐵人賽

DAY 6
0
Software Development

Codetopia 新手日記:設計模式與原則的 30 天學習之旅系列 第 6

Day 6:樣板局——複製勝於創建,但魔鬼藏在深拷貝裡

  • 分享至 

  • xImage
  •  

Codetopia 創城記 (6)|樣板局——複製勝於創建,但魔鬼藏在深拷貝裡

IThome 鐵人賽 設計模式 Prototype Creational Patterns Codetopia

1. 今日熱點 (故事開場 & 痛點)

Codetopia 的清晨六點,城市還在沉睡,但 **樣板局(Prototype Office)**早已燈火通明。

Chloe|樣板管理員的咖啡剛泡好,螢幕上就跳出了今日的挑戰:27 份「社會住宅標準戶型」與 12 份「共享商辦樓層」的緊急申請案。這些需求乍看之下大同小異,細看卻各有玄機——大約 80% 的結構完全相同,剩下那 20% 則需要根據地段、法規進行客製化微調。

如果每一份都從零開始畫設計圖、跑模擬、做驗算......那不僅是初始化成本高到市長會來關切,過程中任何微小的人為失誤,風險都會被無限放大。

Chloe 的方案向來乾脆俐落。她指著辦公室牆上掛著的數十份「核准藍本」說:「不用重新發明輪子。從藍本庫複製一份,再把差異化條款套上去,一鍵就能生成一份可直接送審的新個體。」

聽起來很完美,對吧?(旁白低語:故事如果這麼順利,我就沒戲唱了。)

關鍵的挑戰,也是今天我們要一次踩完、一次填平的坑,全都藏在「複製」這個動作裡:

  • 深拷貝 vs. 淺拷貝:到底該複製到多深?一不小心,就會變成「改了A,B也跟著壞」的靈異事件。

  • 外部資源處理:設計圖引用的外部檔案、網路連線怎麼辦?直接複製句柄,就像給兩戶人家同一把鑰匙,災難的開始。

  • 唯一識別重編:複製出來的新戶型,總得有個新門牌號碼吧?忘了重編,資料庫就要天下大亂了。

(是的,複製貼上,從來就不是簡單的 Ctrl+C、Ctrl+V。)

小小回扣:昨天 Day 5 的都更署靠著 Builder 模式,專注於「分步施工、流程固定」的複雜建案。而今天的樣板局,走的不是一步步蓋的路線,而是先複製一戶精裝修的標準樣品屋,再進場做細節微調。這兩種模式,針對的工程壓力與場景,截然不同。

2. 術語卡

🧭 術語卡(今日導航)

  • Prototype (GoF):以 clone() 方法產生新物件,藉此避免昂貴的初始化過程,並讓呼叫端與具體類別解耦。

  • Deep vs Shallow Copy:深拷貝會遞迴複製所有可變的成員物件,創造一個完全獨立的新個體;淺拷貝則只複製參考(指標),新舊個體會共享內部的可變物件。

  • Template Message / Config Snapshot (EIP/EDA):在中觀層面,將原型視為一個「事件模板」或「設定快照」,以此為基礎派生出具體的事件或任務。

  • Spawn from Blueprint (MAS):在宏觀層面,由一個「代理藍本」來 spawn(派生)出多個擁有相同初始能力與信念的現場代理。

3. 笑中帶淚 (反例/壞味道)

故事很快就出現了轉折。樣板局的客服電話被打爆了:

「喂?我們是 A 區的社宅案,怎麼回事?隔壁 B 區的建案一修改高度限制,我們家的戶型圖也跟著變矮了?!」

讓我們把時間倒帶,看看災難是如何發生的。原來,Chloe 的一位新同事為了求快,直接用了淺拷貝來複製「藍本A」。這份藍本裡的 rules 欄位,是一個共享的可變串列(list)。

後果

  1. 狀態污染:A 案的工程師修改了 rules,因為是共享的,B 案的 rules 也立刻被污染了。

  2. 資源競爭:更糟的是,有人連檔案句柄(file handle)也一起複製了。兩份設計圖搶著寫入同一個暫存檔,最後在驗收系統直接炸裂,上演「檔案已關閉」的戲碼。

  3. ID 撞號clone 完忘了重編唯一的戶型 ID,導致兩份戶型圖用同一個 ID 寫入城市的資料湖,後來的資料直接覆蓋了前者,造成資料品質稽核報告上整片怵目驚心的紅字。

這就是典型的「複製貼上」變「複製災難」。我們來看看這段充滿壞味道的程式碼:

import copy, uuid, tempfile, os, requests

class Plan:
    def __init__(self, rules, id=None, f=None, sess=None, kind="A"):
        self.rules = list(rules)
        self.id = id or str(uuid.uuid4())
        self.f = f          # 外部資源:檔案句柄
        self.sess = sess    # 外部資源:網路連線 Session
        self.kind = kind

    # ❌ 壞味道 1:淺拷貝,連帶複製了 ID 和外部資源句柄
    def clone_bad(self):
        return copy.copy(self)

    # ❌ 壞味道 2:在 clone 方法裡塞滿了業務邏輯分支
    def clone_branch_bad(self):
        p = Plan(self.rules, self.id, self.f, self.sess, self.kind)
        if p.kind == "A":
            p.rules.append({"k": "a_only", "v": "x"})
        elif p.kind == "B":
            p.rules += [{"k": "b1", "v": "y"}]
        return p

# --- 災難現場重現 ---

# 準備一個包含可變規則、檔案句柄、HTTP Session 的基礎藍本
tmp = tempfile.NamedTemporaryFile(delete=False)
base = Plan(
    [{"k": "height_max", "v": "10"}],
    f=tmp,
    sess=requests.Session(),
    kind="B"
)

# ① 互相污染:a 和 base 共享同一個 rules list
a = base.clone_bad()
a.rules.append({"k": "noise", "v": "<=50"})
print(f"淺拷貝後,base 的 rules 也被污染了: {base.rules}")

# ② 資源競爭/雙重關閉:a 和 base 操作同一個檔案
a.f.write(b"a_writes\n")
base.f.write(b"b_writes\n")
a.f.close()
try:
    base.f.write(b"boom\n")
except Exception as e:
    print(f"外部資源競爭,base 想寫入已關閉的檔案: {e}")

# ③ ID 撞號覆蓋:a 和 base 的 ID 完全相同
db = {base.id: base}
db[a.id] = a
print(f"資料筆數 (因為 ID 撞號被覆蓋): {len(db)}")

# ④ clone 內含分支,職責不清
b = base.clone_branch_bad()
print(f"分支膨脹,clone 方法被迫了解業務細節: {b.kind}, rules count: {len(b.rules)}")

# ⑤ 濫用 deepcopy,連第三方資源也想硬拷,直接拋出異常
try:
    copy.deepcopy(base)
except Exception as e:
    # 註記:此處行為視環境而異,但無論是否拋錯,都不應 deepcopy 任何外部連線資源。
    print(f"深拷貝不當,無法複製網路 Session: {e}")

os.unlink(tmp.name)

正解方向clone 的職責應該非常單純——只做「資料結構的選擇性深拷貝」。外部資源由專門的 Provider 或工廠重新取得;唯一 ID 由下游系統或 ID 工廠重新編配;而差異化微調的策略,應該從 clone 方法中移出去。

4. 王牌出手 (核心觀念/何時用/不適用)

在 Chloe 清理完現場後,她召集了所有同事,在白板上寫下了 Prototype 模式的真正精神。

定義:

Prototype 模式,是透過一個「藍本註冊表(Prototype Registry)」取得樣板物件,對樣板呼叫 clone() 方法來產生一個新的個體,最後再對新個體套用所需的「差分(overrides)」。它的核心價值在於,可以大幅降低昂貴的初始化成本(例如:載入複雜的物件圖、大量的 I/O 開銷、耗時的計算預熱),同時也降低了呼叫端對具體類別的耦合。

何時用 (When to Use)

  • ✅ 當物件的初始化成本非常高昂,或其內部結構(物件圖)非常複雜,而新個體大多是基於一個標準樣板進行微調時。

  • ✅ 當你需要避免呼叫端直接 new 一個具體類別時。改由一個註冊表,透過 registry.get("key").clone() 的方式來取得新物件,更具彈性。

  • ✅ 當希望將系統的「缺省狀態」明確化,並以此作為政策或配置快照的基線時。藍本本身就是一份完美的「預設值」文件。

何時不要用 (When NOT to Use)

  • ⛔ 當物件牽涉到外部資源句柄唯一性識別(如 socket、file descriptor、GUID)時。請不要直接複製它們!這些應該改由工廠模式重新申請與配置,以確保資源的正確管理與唯一性。

  • ⛔ 當複製一個物件的成本,其實比重新創建一個還要高的時候。或者,當物件的生命週期與擁有權歸屬不清楚時,直接複製很容易釀成「共享可變狀態」的災難。

  • ⛔ 當 clone() 之後,總是需要接上大量、複雜的條件分支來進行微調時。這意味著差分邏輯可能比 clone 本身還複雜,更適合改用 StrategyDecorator 模式,或將差分策略外移處理。

與 Day 5|Builder 的邊界

  • Builder:專注於分步構建的過程。它的主角是 Director(定義流程)和 Builder(定義工法),適合處理「流程固定,但表現多樣」的複雜物件。

  • Prototype:專注於快速複製標準戶。它的流程不是主角,差分微調才是。它假設已經有一個「完整」的樣板存在。

5. 導播切景 (三層並置圖)

導播, 鏡頭拉一下!讓我們從三個不同的尺度,看看「複製藍本」這件事在 Codetopia 是如何運作的。

如何閱讀:先看微觀層面,一個類別是如何實現 clone 的;再拉到中觀,看看一個「模板事件」是如何派生出具體事件並被派送出去的;最後到宏觀層面,看看一個「代理藍本」是如何派生出多個前線代理,投入城市協作的。

視角 觀念/模式 在城市的說法
微觀 (GoF) Prototype:以 clone 產生新物件 樣板局的「建築藍本」,可被複製與微調
中觀 (EIP/EDA/Actor) Template Message / Config Snapshot 市府發布「事件模板」,各單位填入細節後成為具體事件
宏觀 (MAS) Spawn from Blueprint 由「巡檢代理藍本」 spawn 出多個前線代理
5.1 微觀 (GoF) — Prototype 類別關係圖

https://ithelp.ithome.com.tw/upload/images/20250920/20178500xMNlKbpI31.png

5.2 中觀 (EIP/EDA/Actor) — 模板訊息派生流程

https://ithelp.ithome.com.tw/upload/images/20250920/20178500wXiRxyAo36.png

5.3 宏觀 (MAS) — 由藍本 spawn 多代理

https://ithelp.ithome.com.tw/upload/images/20250920/20178500Et8E29yucb.png

6. 最小實作 (正解)

這是在 Chloe 指導下,同事重構後的程式碼。它清晰地展示了 Prototype 模式的正確實踐方式。

from __future__ import annotations
from copy import deepcopy
from dataclasses import dataclass, field
from typing import Dict, List, Protocol, runtime_checkable

# 定義一個 Prototype 應該要有的行為契約
@runtime_checkable
class Prototype(Protocol):
    def clone(self) -> "Prototype": ...

@dataclass
class Rule:
    key: str
    value: str

# 一個具體的藍本類別,它必須能自我複製
@dataclass
class ZoningPlan(Prototype):
    district: str
    rules: List[Rule] = field(default_factory=list)
    # 範例:外部資源只保留 ID,不複製句柄
    # attachment_id: str | None = None

    def clone(self) -> "ZoningPlan":
        # 關鍵:對內部可變的資料結構(如 list of objects)做「深拷貝」
        # 確保複製出來的新個體,其內部狀態是完全獨立的
        return ZoningPlan(district=self.district, rules=deepcopy(self.rules))

# 藍本註冊表,用來管理所有可供複製的樣板
class PrototypeRegistry:
    def __init__(self) -> None:
        self._store: Dict[str, Prototype] = {}

    def register(self, key: str, proto: Prototype) -> None:
        self._store[key] = proto

    def get(self, key: str) -> Prototype:
        # 這裡回傳的是藍本自身,呼叫端需自行 clone
        return self._store[key]

# --- 正確的使用情境 ---
# 1. 建立並註冊一個基礎藍本
base_plan = ZoningPlan("A", [Rule("height_max", "10"), Rule("green_ratio", ">=30%")])
registry = PrototypeRegistry()
registry.register("Plan#A", base_plan)

# 2. 從註冊表取得藍本,並 clone 出一個新個體
copy1: ZoningPlan = registry.get("Plan#A").clone()  # type: ignore

# 3. 對新個體進行微調,這完全不會影響到原始藍本
copy1.rules.append(Rule("noise_max", "<=50dB"))

# 4. 處理外部資源:交給專門的 Provider
# attachment = FileProvider.fetch(copy1.attachment_id)

# 5. 重新編配唯一識別(例如:由下游系統或 ID 工廠負責)
# new_id = id_factory.generate()
# copy1.id = new_id

# 設計語義:clone 的職責是「結構複製,且不含外部資源句柄」;
# 唯一識別的賦予、外部資源的取得,都交給下游的專職工廠或 Provider。
# --- 結構分離的快速驗證 ---
from copy import deepcopy
assert base_plan.rules is not copy1.rules
_bak = deepcopy(base_plan.rules)
copy1.rules[0].value = "999"
assert base_plan.rules == _bak
print("✅ 結構分離驗證通過:修改複製品不會影響原始藍本。")

7. 鄉民出題 (動手+反模式紅旗)

動手做 & 設計題
  1. 動手做:請為 ZoningPlan 新增一個巢狀的 amenities: List[Amenity] 屬性(Amenity 也是一個 class),並修改 clone 方法以實作真正的深拷貝,最後寫一個分離性測試來驗證修改 copy1.amenities[0] 不會影響到 base_plan.amenities[0]

  2. 設計題:請設計一個 PrototypeRegistry,它要支援兩種取得藍本的方式:一種是「快照凍結」(拿到藍本被註冊那一刻的狀態),另一種是「增量覆蓋」(拿到藍本後,再套用一個 delta 更新檔)。比較這兩種做法在效能與記憶體上的取捨。

  3. 判準題:請列出 5 條你認為應該「立即改用工廠模式,而不是繼續用 clone」的硬性規範。(例如:當物件包含網路連線時、當物件需要唯一序號時...)

反模式紅旗 (Red Flags) 🚩
  • 🚩 淺拷貝共享可變狀態:只要看到 copy.rules is base.rulesTrue,警報就該響了。這意味著兩邊互相污染只是時間問題。

  • 🚩 複製外部資源句柄clone() 方法的實現中,若看到直接複製檔案、Socket 或資料庫連線等欄位,這是一個巨大的風險信號,可能導致資源競爭或雙重關閉。

  • 🚩 clone 後識別未重編:複製出來的多份個體,若其 ID 或唯一鍵完全相同,這將會污染資料湖,是資料品質的殺手。

  • 🚩 把大量條件分支塞進 clone():如果 clone() 方法裡充滿了 if-elif-else 來處理不同型別的特例,代表它的職責太重了,違反了開放封閉原則。

小投票

面對「需要大量相似個體,但又有少量變體」的場景,你的直覺會是?

A. 先建立一個 PrototypeRegistry,把標準樣板管起來。

B. 先做一個「參數化的工廠函式」,用參數來控制變體。

請在留言區選擇 A 或 B,並用一句話說明你的理由!

8. 城市望遠鏡 (進階視野)

  • EIP/EDA (中觀):在事件驅動架構中,我們可以將藍本視為 Template MessageConfig Snapshot。一個上游事件可以只是一個模板,下游的消費者接收後,根據自身情境 clone 並填入具體欄位,生成一個全新的、可執行的事件。

  • Actor/MAS (宏觀):在多代理系統中,這對應著 spawn from blueprint 的概念。一個「藍本代理」持有著標準的能力、信念與初始狀態。當需要部署新的現場代理時,直接從藍本 spawn 出來,確保了所有新代理都有一致的「出廠設定」。

  • 與 Day 5 的協作:當城市需要大量製造相似的元件,並且這些元件還需要「依循相同的流程進一步加工」時,Prototype 和 Builder 可以組成一條高效的生產線。先用 Prototype 快速「產生量」,再把這些半成品交給 Builder 進行「分步深化」。

9. ✅ 回到現場(同一組驗收通過)

讓我們用一套嚴格的驗收腳本,來證明 Chloe 的新流程徹底解決了最初的災難。這套腳本必須驗證:結構的分離性、外部資源的解耦,以及唯一識別的重編

驗收條件

  • 派生戶型必須與原始藍本不共享任何可變的 rules 物件。

  • clone() 的產物不得攜帶任何外部資源句柄(如檔案、Socket)。

  • 派生戶型最終必須擁有一個新的唯一識別(由下游系統編配)。

# --- 驗收腳本 ---
# (接續 6. 最小實作 的 context)

# 驗收 1:分離性 - 記憶體位址不同
assert base_plan.rules is not copy1.rules, "驗收失敗:淺拷貝,共享了同一個 rules 物件!"

# 驗收 2:分離性 - 內容各自獨立
original_rules_copy = deepcopy(base_plan.rules)
copy1.rules[0].value = "999" # 修改複製品
assert base_plan.rules == original_rules_copy, "驗收失敗:深拷貝不完全,修改複制品影響了原始藍本!"

# 驗收 3:外部資源解耦 (示意)
# 假設 clone() 後的物件不應包含 session 屬性
# assert not hasattr(copy1, "session"), "驗收失敗:clone() 攜帶了外部資源句柄!"

print("\n[驗收通過] ✅ 所有複製個體均通過分離性與資源解耦測試!")

# 下游系統再進行 ID 編配與資料登錄...

Chloe 的團隊導入這套流程後,27+12 份設計案在半小時內全數生成並通過驗收,再也沒有發生過「改A壞B」的靈異事件。

10. 測試指北

為了確保這套複製流程的長期穩定,我們需要建立對應的自動化測試。

  • 分離性測試 (Isolation Test):這是最重要的測試。當你修改 copy1.rules[0].value 時,必須斷言 base_plan.rules[0].value 維持不變。這能確保深拷貝的正確性,特別是在處理巢狀物件時。

  • 註冊表契約測試 (Registry Contract Test):驗證 registry.get(key).clone() 絕不會回傳一個與註冊表內藍本共享任何可變結構的物件。clone 是契約的一部分,必須被遵守。

  • 資源規範測試 (Resource Policy Test):透過一個假的 Provider (Fake/Mock) 來驗證 clone() 的產物不會攜帶任何外部資源句柄。例如,斷言 clone 後的物件上找不到 db_connectionfile_handle 這類屬性。

11. 結語 & 預告

二十字摘要:以藍本複製勝於創建;深拷貝防共享,外部資源改由工廠重取。

Chloe 靠著對 Prototype 模式的深刻理解,不僅解決了眼前的危機,還為樣板局建立了一套更穩健的工作流程。但城市的挑戰永無止境,明天,我們將走進城市的「轉接站」,看看工程師們是如何讓那些古老的系統不需修改一行程式碼,也能完美接上最新的介面,敬請期待 Day 7|Adapter:轉接站,讓老系統舊瓶裝新酒!


附錄:ASCII 版圖示

為了確保在不支援 Mermaid 渲染的環境中也能正常閱讀,以下提供文中圖表的 ASCII 替代版本:

A1. 🏗️ 微觀層面 - Prototype 類別關係圖

    ╔═════════════════════════════╗
    ║       🎯 <<interface>>      ║
    ║         Prototype           ║
    ║                             ║
    ║ ➕ clone(): Prototype       ║
    ╚═════════════╤═══════════════╝
                  │ implements
                  ▼
    ╔═════════════════════════════════════════╗
    ║            🏢 ZoningPlan                ║
    ╠═════════════════════════════════════════╣
    ║ 🏷️  district: str                      ║
    ║ 📋 rules: Rule[]                       ║
    ║ 📎 attachments: bytes?                 ║
    ╠═════════════════════════════════════════╣
    ║ ➕ clone(): ZoningPlan                 ║
    ╚═════════════════════════════════════════╝
                  ▲
                  │ stores
    ╔═════════════╧═══════════════════════════╗
    ║        📚 PrototypeRegistry             ║
    ╠═════════════════════════════════════════╣
    ║ ➕ register(key: str,                   ║
    ║              proto: Prototype)          ║
    ║ ➕ get(key: str): Prototype             ║
    ╚═════════════════════════════════════════╝

💡 重點提醒:
   • ZoningPlan 對 rules 做深拷貝 🔄
   • 外部資源不複製句柄,只複製快照 📸
   • 或改以工廠重新取得 🏭

A2. 📡 中觀層面 - 模板訊息派生流程

    👨‍💼 規劃師             📚 藍本註冊表            📤 事件發布器
        ║                      ║                      ║
        ║                      ║                      ║
        ║═══❶ get("Plan#A") ══▶║                      ║
        ║◀══════ proto ════════║                      ║
        ║                      ║                      ║
        ║══❷ clone() ═════════▶║                      ║
        ║◀══════ copy ═════════║                      ║
        ║                      ║                      ║
        ║══❸ apply(overrides) ═══════════════════════▶║
        ║                      ║                      ║
        ║══❹ publish(PlanCreated{copy}) ═════════════▶║
        ║◀═══════════════ ack ════════════════════════║
        ║                      ║                      ║
        ▼                      ▼                      ▼
      ✅ 完成                🔄 待用               📨 已發送

🔄 流程說明:
  ❶ 從註冊表取得藍本原型
  ❷ 呼叫 clone() 產生副本
  ❸ 套用客製化參數
  ❹ 發布為具體事件

A3. 🌐 宏觀層面 - 由藍本 spawn 多代理

       📞 DF               🎯 PlanBlueprint        🏢 SiteAgent[A]     🏢 SiteAgent[B]
    (黃頁服務)                  Agent                                       
        ║                        ║                      ║                ║
        ║                        ║                      ║                ║
        ║═══❶ lookup ═══════════▶║                      ║                ║
        ║   (capability=         ║                      ║                ║
        ║    "blueprint")        ║                      ║                ║
        ║                        ║                      ║                ║
        ║                        ║═══❂ spawnFrom ═════▶║                ║
        ║                        ║    (copyA)           ║                ║
        ║                        ║                      ║                ║
        ║                        ║═══❂ spawnFrom ═════════════════════▶  ║
        ║                        ║    (copyB)           ║                ║
        ║                        ║                      ║                ║
        ║                        ║◀═══✅ ready(A) ═════║                ║
        ║                        ║                      ║                ║
        ║                        ║◀═══✅ ready(B) ═════════════════════  ║
        ║                        ║                      ║                ║
        ▼                        ▼                      ▼                ▼
    📋 已註冊              🎯 藍本就緒             🚀 A區啟動        🚀 B區啟動

🌟 協作流程:
   ❶ DF 尋找可用的藍本代理
   ❂ 藍本代理複製自身並部署到各區
   ✅ 各區代理確認就緒狀態

A4. ⚠️ 複製災難現場示意圖

    📋 Base Plan                      📋 Clone (淺拷貝)
   ╔═══════════════╗                ╔═══════════════╗
   ║ rules: ═══════╬════════════════▶║ rules:        ║
   ║   [Rule1] 📝  ║                ║   [同一個] 📝  ║
   ║   [Rule2] 📝  ║                ║   [物件] 📝    ║
   ║ id: "123" 🏷️  ║                ║ id: "123" 🏷️  ║ ← ❌ ID撞號!
   ║ file: fd1 📁  ║                ║ file: fd1 📁  ║ ← ❌ 資源競爭!
   ╚═══════════════╝                ╚═══════════════╝
          ║                                 ║
          ╚═══════ 💥 共享狀態 ═════════════╝
                    (災難根源)

🚨 災難後果:
   • 修改 Clone.rules[0] → Base.rules[0] 也被改變! 😱
   • 關閉 Clone.file → Base.file 也被關閉! 💀
   • 資料庫出現重複 ID,後蓋前! 🔄

A5. ✅ 正確的深拷貝示意圖

    📋 Base Plan                      📋 Clone (深拷貝)
   ╔═══════════════╗                ╔═══════════════╗
   ║ rules: 📝     ║    🔄 copy     ║ rules: 📝     ║
   ║   [Rule1]     ║   ═════════▶   ║   [Rule1']    ║
   ║   [Rule2]     ║                ║   [Rule2']    ║
   ║ id: "123" 🏷️  ║                ║ id: "456" 🏷️  ║ ← ✅ 重新編配
   ║ file: null 📁 ║                ║ file: null 📁 ║ ← ✅ 不複製句柄
   ╚═══════════════╝                ╚═══════════════╝
          ║                                 ║
          ╚═══════ 🛡️ 獨立狀態 ════════════╝
                    (安全隔離)

🎯 正確實踐:
   • 修改 Clone → Base 不受影響 ✅
   • 資源由工廠重新取得 🏭
   • 各自擁有獨立的 ID 🆔
   • 記憶體完全隔離 🔒

🏆 效益對比:
   ┌────────────┬──────────┬──────────┐
   │    項目     │  淺拷貝   │  深拷貝   │
   ├────────────┼──────────┼──────────┤
   │ 狀態隔離    │    ❌     │    ✅    │
   │ 資源安全    │    ❌     │    ✅    │
   │ ID 唯一性   │    ❌     │    ✅   │
   │ 效能開銷    │    低      │   適中   │
   └────────────┴───────────┴──────────┘


上一篇
Day 5:都更署的秘密武器——Builder 模式搞定複雜物件的分步施工!
系列文
Codetopia 新手日記:設計模式與原則的 30 天學習之旅6
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言