前兩期聚焦技術慣用模式(事務、驗證、安全),本期轉向領域慣用模式——業務邏輯中反覆出現的解決方案。DSL(Domain Specific Language)是捕捉此類模式的利器,核心優勢:**讓代碼看起來像問題領域,而非技術實現**。
需求:H 夫人臥室秘密隔間,開啟序列——關門 → 打開梳妝台第二抽屜 → 開床頭燈。順序錯誤需重置。典型有限狀態機(FSM)。

Event doorClosed = new Event("doorClosed", "D1CL");
Event drawerOpened = new Event("drawerOpened", "D2OP");
// ... 大量變量聲明
State idle = new State("idle");
State active = new State("active");
// ... 手動 addTransition、addAction
machine.addResetEvents(doorOpened);
痛點:冗長、重複變量名、非聲明式、需重編譯部署。
<stateMachine start="idle">
<event name="doorClosed" code="D1CL"/>
<state name="idle">
<transition event="doorClosed" target="active"/>
<action command="unlockDoor"/>
<action command="lockPanel"/>
</state>
<state name="active">
<transition event="drawerOpened" target="waitingForLight"/>
<transition event="lightOn" target="waitingForDrawer"/>
</state>
...
</stateMachine>
優勢:延遲綁定(無需重編譯)、容器語義(巢狀結構)、聲明式。噪音:尖括號、屬性引號、結束標籤。
events
doorClosed D1CL
drawerOpened D2OP
lightOn L1ON
doorOpened D1OP
panelClosed PNCL
end
resetEvents
doorOpened
end
commands
unlockPanel PNUL
lockPanel PNLK
lockDoor D1LK
unlockDoor D1UL
end
state idle
actions {unlockDoor lockPanel}
doorClosed => active
end
state active
drawerOpened => waitingForLight
lightOn => waitingForDrawer
end
...
優勢:極少噪音字符、保留容器語義、聲明式。Fowler 指出:構建時間與 XML 版本相当(ANTLR 等現代工具大幅降低編譯器開發門檻)。
event :doorClosed, "D1CL"
event :drawerOpened, "D2OP"
event :lightOn, "L1ON"
event :doorOpened, "D1OP"
event :panelClosed, "PNCL"
command :unlockPanel, "PNUL"
command :lockPanel, "PNLK"
command :lockDoor, "D1LK"
command :unlockDoor, "D1UL"
resetEvents :doorOpened
state :idle do
actions :unlockDoor, :lockPanel
transitions :doorClosed => :active
end
state :active do
transitions :drawerOpened => :waitingForLight,
:lightOn => :waitingForDrawer
end
...
優勢:零噪音字符(`:` 前綴即 Symbol)、Ruby 區塊原生容器語義、可直接執行/除錯、JRuby 打包為 JAR 部署至 Toaster。
class StateMachineBuilder
attr_reader :machine, :events, :states, :commands
def initialize
@events = {}; @states = {}; @state_blocks = {}; @commands = {}
end
def event(name, code)
@events[name] = Event.new(name.to_s, code)
end
def state(name, &block)
@states[name] = State.new(name.to_s)
@state_blocks[name] = block
@start_state ||= @states[name]
end
def command(name, code)
@commands[name] = Command.new(name.to_s, code)
end
# ... 後續對 block 進行 instance_eval 構建完整狀態機
end
| 特徵 | 含義 | 本案例體現 |
|---|---|---|
| 計算機程式語言 | 有語法、語義、可執行 | XML/自定義語法/Ruby 皆滿足 |
| 語言天性 | 非程式設計師隱約可讀 | 自定義語法/Ruby 版最佳 |
| 領域聚焦 | 僅解決特定問題(FSM 配置) | 無通用控制結構(if/while) |
| 有限表達力 | 無圖靈完整控制流 | 純聲明式,無循環判斷 |
| 非圖靈完整 | 防止意外變通用語言 | XSLT 用於轉換是 DSL,解漢諾塔則非 |
| 維度 | 外部 DSL (ANTLR/Xtext) | 內部 DSL (Groovy/Ruby/Kotlin) |
|---|---|---|
| 語法自由度 | 完全自定義 | 受宿主語法約束 |
| 工具鏈成本 | 需解析器、編輯器、語法高亮 | 復用宿主 IDE 工具鏈 |
| 業務專家可讀性 | 最高(可設計專用符號) | 良好(需接受宿主語法) |
| 執行環境 | 需解析器運行時 | 直接編譯/解釋執行 |
| 類型安全/重構 | 需自建 | 宿主語言原生支援 |
| 團隊技能要求 | 編譯器原理、ANTLR | 宿主語言進階元程式 |
下一期將深入:演化架構主題——架構與設計的邊界、技術政治、Build vs Buy 決策樹、SOA 端點版本控制與寬鬆類型策略。