iT邦幫忙

2025 iThome 鐵人賽

DAY 13
0
Software Development

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

Day 24:Interpreter(直譯器):將「城市法規」編成一門語言,讓機器讀懂人話!

  • 分享至 

  • xImage
  •  

Codetopia 創城記 (24)|Interpreter(直譯器):將「城市法規」編成一門語言,讓機器讀懂人話!

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

午夜 11 點 40 分。窗外,狂風捲著暴雨,猛烈地敲打著城市控制中心的落地窗,彷彿要將整座城市吞噬。室內,空氣卻因另一場風暴而凝滯——刺耳的警報聲與此起彼落的鍵盤敲擊聲交織成一片焦灼的協奏曲。螢幕上,氣象局的豪雨特報紅得發亮,旁邊不斷刷新的,是市民中心被打爆的鄰里噪音陳情紀錄。

一場僅有代號的臨時線上會議,在幾分鐘內火速通過了一份名為「夜間噪音與人流管制條例(草案 v0.1)」的數位文件。條文的邏輯密度高得令人窒息,字裡行間纏繞著氣象等級、管制時段、指定區域、舞台音響狀態,甚至還關聯到前一階段封路的完成狀態……

市長的聲音透過揚聲器傳來,語氣不帶一絲溫度,卻像一道指令烙印在每個人的神經末梢:「十分鐘內,落地執行。」

命令下達的瞬間,災難正式拉開序幕。控制中心的工程師們,彷彿被拋入了一座數位迷宮,一座由無數 if/else 磚塊砌成的混亂叢林:

  • 交管局的螢幕:「雨量大於 50mm 而且 噪音超過 75 分貝,就封鎖 A 區……應該是這樣吧?」一位年輕工程師喃喃自語,游標在提交按鈕上猶豫不決。

  • 電力處的頻道:「我的指令稿是看到『舞台』而且 時間戳過 22:00 就斷電,沒錯吧?誰能確認一下?」

  • 餐車管理科的即時訊息:「我這邊的邏輯是,只要人流密度 > 60% 或者 收到豪雨警報,就引導餐車……等一下,『或者』的優先級是什麼?」

同一份條例,在不同的終端化為截然不同的解讀。真理,在此刻碎裂成無數個版本,沒有誰能說清,到底哪個才是那唯一的真實來源(SSOT)。

就在混亂即將觸發第一條錯誤指令時,會議頻道的一個角落,響起一個異常冷静的聲音。Ada|條例語法設計師 推了推眼鏡,鏡片反射著滿屏的錯誤日誌,她說:「各位,方向錯了。我們不該用程式碼去追逐條例,而是要讓機器,直接讀懂條例。」

話音剛落,她身旁的 Hugo|規則引擎工程師 闔上筆電,發出清脆的一聲。他環視眾人,嘴角帶著一絲從容:「Ada 說的沒錯。只要妳能把條例的邏輯編成一套語法,給我一棵 AST(抽象語法樹),我保證五分鐘內,讓直譯器接管這裡的一切。而且,它能完美地與我們前幾天剛建好的號誌(State)SOP(Template Method)、**資產巡檢(Visitor)時光機(Memento)**基建無縫對接。」

一瞬間,鍵盤聲停了下來。所有人的目光,穿過數據的洪流,聚焦在這兩位看似平凡的市民英雄身上。

2) 術語卡 🧭

  • GoF|Interpreter:為某個特定領域的語言(DSL)定義其文法,並建立一個直譯器來解釋該語言中的句子。在一個上下文(Context)中對表示式(Expression)求值,最終得到決策或動作。

  • EIP/EDA|Content-Based Router / Rules Engine:事件或訊息,根據其內容(Payload)通過規則引擎的求值,被路由至對應的處理管線。這套機制通常支援規則版本化與灰度釋出。

  • MAS|OrdinanceAgent:一個專門的「條例代理」,它會向黃頁服務(Directory Facilitator)註冊自己擁有「解讀城市條例並下達命令」的能力,並與其他代理(如交管代理)進行協作。

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

在 Ada 和 Hugo 出手前,各局處的實作堪稱一場「口耳相傳條例學」的災難。每個人都複製貼上一份自己理解的規則,然後開始微調:

# bad_smell_policy.py

def apply_emergency_rules(area, current_env):
    # 條例版本?執行口徑?全靠口頭約定和開發默契…
    # 交管局的版本
    if current_env.is_raining and current_env.hour >= 22:
        if area.has_stage:
            power.turn_off(area.stage)  # 電力處的人說先斷電

        if current_env.get_metric('noise_db') > 75:
            api.broadcast("請降低音量,遵守夜間安寧")  # 宣導組加上的邏輯

    # 餐車科自己加的
    if area.has_foodtrucks and current_env.get_metric('crowd_density') > 0.6:
        foodtrucks.reroute_to("Zone C") # 有人說先移車比較重要

    # ...底下還有數不清的 if/else 變體,散落在各個微服務中

    audit_log("Rules applied for " + area.name) # Done 是 done 了,但根本不知道是依據哪條 done 的

壞味道清單

  • 規則分身繁殖:同一條例,N 個實作版本,語義不一致。

  • 邏輯與實作強耦合:規則的任何變動,都意味著要修改、測試、重新部署程式碼。

  • 無法審計與回溯:出事了,你根本無法追溯當時是根據哪一版的「口頭條例」做的決策。(「時光局」的 Memento 在此完全派不上用場)

  • 與城市基建失聯:這些各自為政的 if 判斷,完全繞過了我們之前建立的 State(狀態機)、Template Method(標準作業流程)和 Visitor(資產巡檢)模式。

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

Interpreter 模式的核心思想,就是把一個經常變動、但有內在文法結構的問題,從「程式碼」的維度,提升到「語言」的維度來解決

我們不寫死 if/else,而是設計一門「條例描述語言 (DSL)」。接著,我們寫一個「直譯器」來讀懂這門語言。

在 Codetopia 的場景裡,一個工程級的落地流程是這樣的:

  1. 定義 DSL:Ada 設計出一套描述「當 (WHEN) 符合某些條件 (Conditions),則 (THEN) 執行某些動作 (Actions)」的簡單語法。語法規則中,運算子優先序被明確定義 (NOT > AND > OR),且所有時間求值一律以法定時區 Asia/Taipei 為準,時間欄位在解析階段就轉為標準時間物件,嚴禁字串比較。

  2. 解析與求值:Hugo 的直譯器會把 DSL 文字解析成一棵 AST (抽象語法樹)。Parser 必須設有安全護欄:token 白名單、最大長度、最大 AST 深度、禁止內嵌程式碼與副作用。

  3. 在上下文中執行:直譯器帶著當前的「上下文 (Context)」(其中 noise_db / rain.level 等指標的單位與精度有明確定義),遍歷 AST 進行求值。缺值策略:任何指標缺失一律記錄為 Unknown,在求值語意上視為不觸發,並寫入審計欄位 missing_metrics=[...]

  4. 產出標準化命令:求值結果不是直接執行,而是產生一個標準化的「命令 (Command) 列表」。每一條命令都帶有 ordinance_version、作用範圍 scopeidempotency_key,確保操作可追溯且冪等。

  5. 接入城市基建:這個列表再交由我們熟悉的 Template Method (SOP) 來執行。

    • 多規則合流策略:依 (priority 降序, create_time 升序, rule_id 升序) 排序,預設命中即 ACCUMULATE;若規則標註 STOP_ON_MATCH 則終止後續求值。

    • SOP 層的持久化機制會利用 idempotency_key 做去重和審計。

    • 執行前後用 Memento 拍下內部狀態快照。

    • 對於像廣播(Broadcast)這類不可逆的外部動作,會走 Saga 補償路徑;Memento 只負責保存內部狀態,不假裝能倒轉外部世界。

何時用 (When to Use)

  • 規則穩定且可抽象化:當你的業務邏輯可以被歸納為少數幾個運算子(如 AND, OR, >=, IN)和操作的組合時。

  • 需要跨系統一致性:當同一套規則需要在多個不同系統、不同團隊中以完全相同的口徑執行時,DSL 就是那個「唯一真實來源」。

  • 需要審計與可回放:DSL 規則本身是文字,可以被版本化、儲存、持久化。AST 也可以被序列化。審計紀錄範例

    {
      "ordinance_version": "v2025-10-08.2",
      "ast_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
      "context_digest": "5c9a1b210cf192d1a1b3a2f8d1b1a7f4d2f1a3b1a8f3a3e2e8f1a3b1a8f3a3e2",
      "evaluated_at": "2025-10-08T23:55:00+08:00",
      "cmd_digest": "c7a1f1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0",
      "correlation_id": "ord-20251008-xyz123"
    }
    

何時不要用 (When NOT to Use)

  • 規則變體爆炸:如果語法變得極其複雜,條件分支多到需要完整的編譯器前端技術(如 LALR 剖析)時,手寫 Interpreter 會變得難以維護。這時應該考慮引入專業的規則引擎(如 Drools)或剖析器生成工具(如 ANTLR)。

  • 只是簡單策略切換:如果僅僅是替換一個演算法或單一步驟的邏輯,使用 Strategy 模式 會更輕量、更直覺。沒必要為了選 A 或 B 而發明一門語言。

5) 導播切景 (表格+兩張 Mermaid) 🎥

導播,鏡頭拉一下!讓我們用三個層次來看看這個「條例直譯器」在 Codetopia 的定位。

視角 觀念/模式 在 Codetopia 的說法
微觀(GoF) Interpreter (Expression / Context) Expression 類別階層(比較/布林/事件)、ActionListContext(包含環境變數與資產狀態)
中觀(EIP/EDA) Content-Based Routing / Rules Engine OrdinanceRouter 將事件依條例求值結果,導向對應的 SOP 節點或命令匯流排。
宏觀(MAS) OrdinanceAgent + DF 條例代理透過黃頁(DF)查找可執行的局處代理,並在需要回滾時呼叫時光局(TimeBureau)。

微觀 GoF 層級:條例表示式 (Expression) 結構

這張圖展示了 Ada 如何將條例的文法,拆解成一個個可以被組合的「表示式」物件。

中觀 EIP/EDA 層級:條例求值與執行流程

這張圖展示了 Hugo 設計的完整處理流程,從一條 DSL 規則文字,到最終觸發標準作業流程。

https://ithelp.ithome.com.tw/upload/images/20251009/20178500LGBR5kpCDF.png

6) 最小實作 (英雄代碼) 💻

面對混亂,Hugo 沒有猶豫。他接收了 Ada 設計的 AST 結構,迅速地將其轉化為具備型別安全、時區感知、跨日邊界、缺值保護和冪等性考量的 Python 實作。這不再是虛擬碼,而是能在那個風雨交加的夜晚,穩定 Codetopia 的英雄代碼。

# Production-ready implementation by Hugo
# (Conceptually separated into types.py, context.py, expr.py, interpreter.py)

from dataclasses import dataclass
from datetime import time, datetime, timedelta
from typing import Any, Dict, List, Tuple, Optional
import hashlib
from zoneinfo import ZoneInfo

# --- types.py: 定義核心資料結構 ---
@dataclass(frozen=True)
class Command:
    name: str
    params: Tuple[Any, ...]
    ordinance_version: str
    scope: Tuple[str, ...] = ()
    effective_ts: Optional[datetime] = None

    def idempotency_key(self) -> str:
        """產生唯一的冪等鍵,用於持久層去重。不包含時間戳以確保可重放性。"""
        raw = f"{self.name}|{self.params}|{self.ordinance_version}|{self.scope}"
        return hashlib.sha256(raw.encode()).hexdigest()

# --- context.py: 提供安全的求值環境 ---
class Context:
    def __init__(self, env: Dict[str, Any], assets: Dict[str, Any], zone_index):
        self.env = env
        self.assets = assets
        self.zone_index = zone_index

    def metric(self, key: str, default: Optional[float] = None) -> Optional[float]:
        v = self.env.get(key, default)
        try:
            return float(v) if v is not None else None
        except (TypeError, ValueError):
            return None  # 缺值或型別錯誤 -> 視為 Unknown

    def now(self) -> datetime:
        dt = self.env["local_time"]
        assert dt.tzinfo is not None, "local_time 必須是 tz-aware datetime"
        return dt

    def time_at(self) -> time:
        """將時間標準化為『法定時區 Asia/Taipei』的 naive time 以利比較"""
        return self.now().astimezone(ZoneInfo("Asia/Taipei")).time().replace(tzinfo=None)

    def in_zone(self, zone_name: str) -> bool:
        return self.zone_index.contains(self.assets.get("location"), zone_name)

    def aggregate_max(self, metric: str, window: timedelta) -> Optional[float]:
        # 在真實環境中,此處會查詢時序資料庫。此處為 stub。
        return self.metric(metric)

# --- expr.py: 定義表達式節點與求值邏輯 ---
class Expr:
    def eval(self, ctx: Context) -> bool: ...

class And(Expr):
    def __init__(self, l: Expr, r: Expr): self.l, self.r = l, r
    def eval(self, ctx: Context) -> bool: return self.l.eval(ctx) and self.r.eval(ctx)

class Or(Expr):
    def __init__(self, l: Expr, r: Expr): self.l, self.r = l, r
    def eval(self, ctx: Context) -> bool: return self.l.eval(ctx) or self.r.eval(ctx)

class Not(Expr):
    def __init__(self, x: Expr): self.x = x
    def eval(self, ctx: Context) -> bool: return not self.x.eval(ctx)

class GteMetric(Expr):
    def __init__(self, metric: str, value: float): self.m, self.v = metric, value
    def eval(self, ctx: Context) -> bool:
        mv = ctx.metric(self.m)
        return False if mv is None else mv >= self.v  # 缺值視為不觸發

class TimeBetween(Expr):
    """支援跨日:如 22:00–06:00"""
    def __init__(self, start_hhmm: str, end_hhmm: str):
        sh, sm = map(int, start_hhmm.split(":"))
        eh, em = map(int, end_hhmm.split(":"))
        self.s, self.e = time(sh, sm), time(eh, em)

    def eval(self, ctx: Context) -> bool:
        t = ctx.time_at()
        if self.s <= self.e: # 同一天
            return self.s <= t <= self.e
        else: # 跨午夜
            return t >= self.s or t <= self.e

class InZone(Expr):
    """區域判斷:抽象成 predicate,由 Context 提供具體實作(GIS 或資產標籤)"""
    def __init__(self, zone: str):
        self.zone = zone
    def eval(self, ctx: Context) -> bool:
        return ctx.in_zone(self.zone)

class WithinOver(Expr):
    """時間窗內的最大值是否超標;例:noise.db 在 5 分鐘內是否曾 > 75dB"""
    def __init__(self, metric: str, threshold: float, window: timedelta):
        self.metric, self.threshold, self.window = metric, threshold, window
    def eval(self, ctx: Context) -> bool:
        v = ctx.aggregate_max(self.metric, self.window)
        return False if v is None else v > self.threshold

# --- interpreter.py: 核心直譯器 ---
class Interpreter:
    def __init__(self, rule_ast: Expr, actions_ast: List[Tuple[str, Tuple[Any, ...]]], ordinance_version: str, scope: Tuple[str, ...] = ()):
        self.rule, self.actions, self.ver, self.scope = rule_ast, actions_ast, ordinance_version, scope

    def run(self, ctx: Context) -> List[Command]:
        if not self.rule.eval(ctx):
            return []
        ts = ctx.now()
        return [Command(n, p, self.ver, self.scope, ts) for (n, p) in self.actions]

# --- 模擬執行 ---
# 假設 Parser 已將 DSL
#   WHEN rain.level >= 2.0 AND time BETWEEN "22:00" AND "06:00" AND IN Zone("Harbor")
#   THEN  PowerOff("Stage"), PauseTraffic()
# 轉換為 AST
rule_ast = And(
    And(GteMetric("rain.level", 2.0), TimeBetween("22:00", "06:00")),
    InZone("Harbor")
)
actions_ast = [("PowerOff", ("Stage",)), ("PauseTraffic", ())]
ordinance_version = "v2025-10-08.2"
scope = ("zone:Harbor",)

class MockZoneIndex:
    def contains(self, location, zone_name): return True

# 建立帶有 tz-aware datetime 的上下文
tz = ZoneInfo("Asia/Taipei")
live_context = Context(
    env={"rain.level": 3.0, "local_time": datetime(2025, 10, 8, 23, 55, 0, tzinfo=tz)},
    assets={"location": "Harbor"},
    zone_index=MockZoneIndex()
)

interpreter = Interpreter(rule_ast, actions_ast, ordinance_version, scope)
commands_to_run = interpreter.run(live_context)

print("--- 產生的命令列表 ---")
for cmd in commands_to_run:
    print(f"- Command: {cmd.name}{cmd.params}, Ver: {cmd.ordinance_version}, Scope: {cmd.scope}")
    print(f"  Idempotency Key: {cmd.idempotency_key()[:10]}...")

# (可選)計算一份最小審計紀錄,方便重播與追查
audit = {
    "ordinance_version": ordinance_version,
    "ast_sha256": hashlib.sha256(repr(rule_ast.__class__.__name__).encode()).hexdigest(),  # 真實環境請序列化 AST
    "context_digest": hashlib.sha256(str(live_context.env).encode()).hexdigest(),
    "evaluated_at": live_context.now().isoformat(),
    "cmd_digest": hashlib.sha256("|".join(c.idempotency_key() for c in commands_to_run).encode()).hexdigest(),
}
print("audit:", {k: (v[:10] + "...") if isinstance(v, str) and len(v) > 14 else v for k, v in audit.items()})

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

現在,輪到你來扮演 Codetopia 的工程師,鞏固今天的學習!

  1. 情境驗收題

    • Given: 一條新的 DSL 規則 WHEN crowd.density > 0.8 OR env.is_typhoon_signal, THEN Evacuate("All"), Notify("Emergency")

    • When: Context 的環境是 {"crowd.density": 0.9, "is_typhoon_signal": false, "local_time": ...}

    • Then: 請問 Interpreter 最終會產出什麼命令列表?Notify 這個不可逆命令,如果執行失敗,你會如何設計它的「補償」動作?

  2. 回到「笑中帶淚」的現場:

    如果你是當時接手 apply_emergency_rules 函數的工程師,你會如何說服你的同事,放棄 if/else 地獄,改用 Ada 和 Hugo 提出的 Interpreter 方案?請條列出 3 個最有說服力的優點。

  3. 反模式紅旗大哉問

    • 🚩 紅旗一:團隊有人提議:「為了效能,我們讓 Interpreter 直接把 DSL 轉譯成一長串 if/else 的 Python 程式碼再 exec() 怎麼樣?」這會掉回什麼陷阱裡?(提示:安全、審計、可維護性)

    • 🚩 紅旗二:有人為了方便,把條例版本號 v2025-10-08.1 當作一個字串常數寫死在程式碼裡。為什麼這是個極度危險的壞味道?(提示:審計、灰度發布)

    • 🚩 紅旗三:回顧 Day 23,如果 Memento 的快照(Caretaker)做得過於肥大,把整個 App 的狀態都存進去,在這次的緊急回滾場景中,會引發什麼新的災難?

8) 測試矩陣 (Testing Matrix) 🧪

為了確保條例系統的穩定,Hugo 還留下了一份測試矩陣指南:

  • 語法金樣測試:確保 Parser 能正確處理運算子優先序(NOT > AND > OR)、括號、多條件組合。

  • 語意金樣測試:驗證時間跨日、臨界值(>= vs >)、集合包含、多區域等邊界條件的求值結果是否正確。

  • 冪等與重放測試:確保相同的 Context 多次求值產生的命令不變;SOP 層重播命令時無副作用。

  • 版本切換測試:驗證同一個事件,在 v1 和 v2 版本的規則下,求值結果的差異能被審計系統準確重現。

9) 城市望遠鏡 (進階視野) 🔭

今天我們手刻了一個迷你直譯器,但這個模式的視野可以放得更遠。在更宏大的架構中,它會演化成一個成熟的規則引擎 (Rules Engine)。在多代理系統 (MAS) 的世界裡,每個代理都可以載入不同的「條例組 (Policy)」,並用內建的 Interpreter 來決定自己的行為,實現真正去中心化、基於規則的自治協作。

10) 結語 & 預告 ✨

午夜 11 點 55 分,在 Ada 和 Hugo 的聯手下,混亂的 if/else 被一套優雅、健壯的 DSL 和直譯器取代。同一份條例,在所有系統中以完全一致的口徑被求值、執行。城市的應急系統,第一次擁有了一套可審計、可回放、可版本化的「數位法規」。

今日核心:與其用程式碼去追逐易變的規則,不如將規則本身變成一門語言。

有了語言和法律,下一步就是確保我們的「建築工人」(程式碼)本身也遵紀守法。明天,我們將深入 Codetopia 的城市憲法!

明日預告:Day 25|SOLID I (單一職責/開閉原則) —— 讓程式碼守法的第一步,從專心做好一件事開始!


附錄:ASCII 版圖示

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

微觀 GoF 層級:條例表示式 (Expression) 結構

                    ┌─────────────────────┐
                    │    Context          │
                    │ ┌─────────────────┐ │
                    │ │ +env_vars       │ │
                    │ │ +assets         │ │
                    │ │ +now() datetime │ │
                    │ │ +in_zone() bool │ │
                    │ │ +metric() float │ │
                    │ └─────────────────┘ │
                    └─────────────────────┘
                              │
                              │ provides context to
                              ▼
            ┌────────────────────────────────────────┐
            │    <<interface>>                       │
            │    AbstractExpression                  │
            │ ┌────────────────────────────────────┐ │
            │ │ +eval(Context) → bool              │ │
            │ └────────────────────────────────────┘ │
            └────────────────────────────────────────┘
                              △
                 ┌────────────┼────────────┐
                 │            │            │
         ┌───────▼─────┐ ┌────▼────┐ ┌─────▼────┐
         │AndExpression│ │OrExpr..│ │NotExpr.. │
         │ ┌─────────┐ │ │        │ │          │
         │ │ +eval() │ │ │+eval() │ │ +eval()  │
         │ └─────────┘ │ │        │ │          │
         └─────────────┘ └─────────┘ └──────────┘
                 │                       │
                 │ composed of           │ composed of
                 ▼                       ▼
         ┌───────────────┐       ┌──────────────────┐
         │TimeBetween    │       │ Other Concrete   │
         │Expression     │       │ Expressions...   │
         │ ┌───────────┐ │       │                  │
         │ │ +eval()   │ │       │                  │
         │ └───────────┘ │       │                  │
         └───────────────┘       └──────────────────┘

註:提供安全的求值環境,處理時區與缺值

中觀 EIP/EDA 層級:條例求值與執行流程

┌─────────────────────────────┐
│ DSL 規則文字 (v2025-10-08.1) │
└──────────────┬──────────────┘
               │
               ▼
┌──────────────────────────────┐     ┌─────────────────────┐
│ Parser (含類型轉換)            │────▶│ AST (抽象語法樹)      │
└──────────────────────────────┘     └──────────┬──────────┘
                                                │
    ┌──────────────────────────────────────────────────────────┐
    │              Hugo's Interpreter                          │
    │  ┌─────────────────────────┐        ┌─────────────────┐ │
    │  │ Context                 │───────▶│   ast.eval()    │ │
    │  │ (tz-aware, 帶缺值處理)    │        └─────────────────┘ │
    │  └─────────────────────────┘                 │          │
    └────────────────────────────────────────────────┼────────┘
                                                    │
                                                    ▼
              ┌───────────────────────────────────────────────────┐
              │ [Cmd_1, Cmd_2, ...] (帶 scope/ts/冪等鍵)           │
              └───────────────────┬───────────────────────────────┘
                                  │
                                  ▼
              ┌───────────────────────────────────────────────────┐
              │ Day 21 的 Template Method (SOP)                   │
              └───────────────────┬───────────────────────────────┘
                                  │
    ┌─────────────────────────────────────────────────────────────────┐
    │                     SOP 執行骨架                                │
    │                                                                │
    │  ┌─────────────────────┐                                       │
    │  │ Day 23 Memento:     │                                       │
    │  │ 拍下 pre-快照        │                                       │
    │  └──────────┬──────────┘                                       │
    │             │                                                  │
    │             ▼                                                  │
    │  ┌─────────────────────┐                                       │
    │  │ 持久層冪等性檢查     │                                       │
    │  │ & 分流              │                                       │
    │  └──────────┬──────────┘                                       │
    │             │                                                  │
    │             ▼                                                  │
    │  ┌─────────────────────┐      ┌─────────────────────────────┐  │
    │  │ 執行命令            │─成功──▶│ Day 23 Memento: 提交         │  │
    │  │ (不可逆則觸發補償)   │      └─────────────────────────────┘  │
    │  └──────────┬──────────┘                                       │
    │             │                                                  │
    │             │失敗                                              │
    │             ▼                                                  │
    │  ┌─────────────────────────────┐                               │
    │  │ Day 23 Memento: 回滾        │                               │
    │  │ & 觸發補償                  │                               │
    │  └─────────────────────────────┘                               │
    └─────────────────────────────────────────────────────────────────┘

圖例: ▶ = 流程方向  ─ = 連接線  □ = 處理步驟  ◇ = 決策點

表格:Codetopia 條例直譯器的三層視角

┌─────────────┬──────────────────────────────┬─────────────────────────────────┐
│   視角      │      觀念/模式               │     在 Codetopia 的說法          │
├─────────────┼──────────────────────────────┼─────────────────────────────────┤
│ 微觀(GoF)   │ Interpreter                  │ Expression 類別階層             │
│             │ (Expression / Context)       │ (比較/布林/事件)、ActionList、   │
│             │                              │ Context(包含環境變數與資產狀態)   │
├─────────────┼──────────────────────────────┼─────────────────────────────────┤
│中觀(EIP/EDA)│ Content-Based Routing /      │ OrdinanceRouter 將事件依條例     │
│             │ Rules Engine                 │ 求值結果,導向對應的 SOP 節點     │
│             │                              │ 或命令匯流排                    │
├─────────────┼──────────────────────────────┼─────────────────────────────────┤
│ 宏觀(MAS)   │ OrdinanceAgent + DF          │ 條例代理透過黃頁(DF)查找可執行   │
│             │                              │ 的局處代理,並在需要回滾時呼叫   │
│             │                              │ 時光局(TimeBureau)              │
└─────────────┴──────────────────────────────┴─────────────────────────────────┘

核心概念示意圖:從規則到執行

規則文字                AST 結構              命令產出              基建整合
─────────              ──────────            ─────────             ─────────

WHEN rain.level        ┌─────────┐           ┌─────────┐           ┌─────────┐
>= 2.0 AND            │   AND   │          │PowerOff │──┐        │Template │
time BETWEEN          │  /   \  │          │ Stage   │  │   ────▶│ Method  │
"22:00"-"06:00"  ════▶ │ >=   TB │   ════▶  └─────────┘  │        │  (SOP)  │
AND IN Zone           │rain  22 │          ┌─────────┐  │        └─────────┘
"Harbor"              │.lvl  :00│          │Pause    │  │             │
                      └─────────┘          │Traffic  │  │             ▼
THEN PowerOff,                             └─────────┘──┘        ┌─────────┐
PauseTraffic                                                     │Memento  │
                                                                 │快照機制  │
                                             每個命令都帶有:       └─────────┘
                                             • ordinance_ver
                                             • scope 範圍
                                             • idempotent_key
                                             • timestamp

圖例: ══▶ = 解析轉換  ──▶ = 資料流  TB = TimeBetween  >= = GteMetric
```-

上一篇
Day 23:Memento(時光局):尖峰時段的一鍵「回到剛剛」
系列文
Codetopia 新手日記:設計模式與原則的 30 天學習之旅24
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言