(AI 创作) 设计模式 in MoonBit

对比学习用, 没有详细核对.

  • 参考 Clojure Design Patterns 的思路:模式在函数式/代数数据类型语言里会“变形”,重点不是照搬 OO 模板,而是理解问题与解法。原文中明确讨论了多种经典模式在 Clojure 中的函数式写法,比如把 Strategy 看成传函数、Observer 看成回调、Interpreter 看成处理树的一组函数、Facade 看成单一入口等。(mishadoff.com)
  • MoonBit 写法为唯一代码展示
  • 完整覆盖 23 个 GoF 模式
  • 文章风格偏博客,而不是纯字典条目

MoonBit Design Patterns:用 MoonBit 重新理解 23 个 GoF 设计模式

很多开发者第一次接触设计模式,学到的是 Java、C++、C# 这类经典面向对象写法:接口、抽象类、继承层级、工厂、访问者、单例、装饰器……
但当我们来到 MoonBit 这样更现代、更加重视代数数据类型、模式匹配、函数作为一等公民、模块边界与值建模的语言时,会很快发现一件事:

设计模式并没有消失,只是“形状变了”。

这也是我很认同的一种看法:设计模式本质上不是某种固定语法模板,而是“反复出现的问题与解法命名”。参考那篇讲 Clojure 的文章,很多 GoF 模式在函数式语境下都会被简化、压缩,甚至被语言特性部分吸收。比如:

  • Strategy 往往就是传函数
  • State 往往就是 enum + match
  • Interpreter 往往就是 AST + evaluator
  • Facade 往往就是 一个统一入口函数
  • Observer 往往就是 回调列表或事件流

这篇文章就尝试做一件事:

用 MoonBit 重写 23 个 GoF 设计模式的理解方式。

这里不展示其他语言,不做 Java 对照,也不刻意模仿传统 OO 样板;
我们只讨论一个问题:

如果用 MoonBit 写,这个模式自然会长成什么样?


先说结论:MoonBit 里的设计模式该怎么学

在 MoonBit 里,学习设计模式最重要的不是背类图,而是先记住这几个映射:

  • 变化的行为 → 传函数 / record 中保存函数
  • 变化的状态enum + match
  • 树形结构 → 递归代数数据类型
  • 解释规则 → AST + 递归求值
  • 创建逻辑 → 工厂函数 / builder
  • 共享数据 → 不可变值 + 缓存池
  • 层次包裹能力 → 小 record 包装 + 转发
  • 流程钩子 → 骨架函数 + 注入步骤
  • 事件通知 → observers 回调列表
  • 命令和操作历史enum 命令值 / 函数值 / 快照

所以这篇文章虽然会完整介绍 23 个 GoF 模式,但你会看到:
很多模式到了 MoonBit 里,已经不再需要沉重的“接口 + 继承 + 虚方法”才能实现。


GoF 设计模式总览

GoF 通常把 23 个模式分为三类:

创建型

  1. Abstract Factory
  2. Builder
  3. Factory Method
  4. Prototype
  5. Singleton

结构型

  1. Adapter
  2. Bridge
  3. Composite
  4. Decorator
  5. Facade
  6. Flyweight
  7. Proxy

行为型

  1. Chain of Responsibility
  2. Command
  3. Interpreter
  4. Iterator
  5. Mediator
  6. Memento
  7. Observer
  8. State
  9. Strategy
  10. Template Method
  11. Visitor (gofpattern.com)

下面逐一展开。


一、创建型模式


1. Abstract Factory

意图

提供一个接口,用于创建一组相关对象,而不暴露具体实现细节。

传统 OO 里它经常长这样:

  • 抽象工厂接口
  • 多个具体工厂
  • 每个工厂负责创建一整套彼此兼容的产品

在 MoonBit 里,最自然的方式通常不是“抽象类工厂”,而是:

  • 一个 struct,里面放若干创建函数;
  • 或一个 enum Theme,再统一派发。

MoonBit 写法

struct Button {
  text : String
}

struct Dialog {
  title : String
}

struct WidgetFactory {
  make_button : (String) -> Button
  make_dialog : (String) -> Dialog
}

fn light_factory() -> WidgetFactory {
  {
    make_button: fn(text) { { text: "[Light] " + text } },
    make_dialog: fn(title) { { title: "[Light] " + title } },
  }
}

fn dark_factory() -> WidgetFactory {
  {
    make_button: fn(text) { { text: "[Dark] " + text } },
    make_dialog: fn(title) { { title: "[Dark] " + title } },
  }
}

fn render_page(factory : WidgetFactory) -> Unit {
  let btn = factory.make_button("Submit")
  let dlg = factory.make_dialog("Settings")
  println(btn.text)
  println(dlg.title)
}

什么时候适合

当你要保证“一整套对象”的风格或行为一致时,例如:

  • UI theme
  • 数据库方言
  • 序列化协议族
  • 测试环境 / 正式环境的对象组装

MoonBit 视角

Abstract Factory 在 MoonBit 中最重要的不是“工厂接口”,而是:

把一组相关的创建策略打包成一个值。


2. Builder

意图

分步骤构造复杂对象,把对象构造过程和对象表示分离。

在 MoonBit 这种支持 record 的语言里,很多对象其实直接构造就够了。
但当对象具备以下特点时,Builder 仍然有价值:

  • 字段很多
  • 有默认值
  • 构造顺序受约束
  • 需要校验
  • 构造过程要可读

MoonBit 写法

struct HttpRequest {
  method : String
  url : String
  headers : Map[String, String]
  body : String
}

struct HttpRequestBuilder {
  mut method : String
  mut url : String
  mut headers : Map[String, String]
  mut body : String
}

fn new_request_builder() -> HttpRequestBuilder {
  {
    method: "GET",
    url: "",
    headers: {},
    body: "",
  }
}

fn method(b : HttpRequestBuilder, m : String) -> HttpRequestBuilder {
  b.method = m
  b
}

fn url(b : HttpRequestBuilder, u : String) -> HttpRequestBuilder {
  b.url = u
  b
}

fn header(b : HttpRequestBuilder, k : String, v : String) -> HttpRequestBuilder {
  b.headers[k] = v
  b
}

fn body(b : HttpRequestBuilder, s : String) -> HttpRequestBuilder {
  b.body = s
  b
}

fn build(b : HttpRequestBuilder) -> HttpRequest {
  {
    method: b.method,
    url: b.url,
    headers: b.headers,
    body: b.body,
  }
}

MoonBit 视角

Builder 在 MoonBit 里通常不是为了解决“构造器重载爆炸”,而是为了解决:

  • 可读性
  • 默认值
  • 过程校验
  • 分阶段构造

3. Factory Method

意图

定义一个创建对象的接口,让子类决定实例化哪一个类。

但在 MoonBit 里,Factory Method 往往根本不需要“子类”:

一个普通工厂函数就够了。

MoonBit 写法

enum Logger {
  Stdout
  Silent
}

fn make_logger(kind : String) -> Logger {
  match kind {
    "stdout" => Stdout
    "silent" => Silent
    _ => Stdout
  }
}

fn log(logger : Logger, msg : String) -> Unit {
  match logger {
    Stdout => println(msg)
    Silent => ()
  }
}

更函数式一点的版本

fn make_printer(kind : String) -> (String) -> Unit {
  match kind {
    "stdout" => fn(msg) { println(msg) }
    "silent" => fn(_msg) { () }
    _ => fn(msg) { println(msg) }
  }
}

MoonBit 视角

Factory Method 的核心从来不是“继承”,而是:

把创建逻辑集中起来,让调用方只依赖抽象结果。


4. Prototype

意图

通过复制现有对象来创建新对象,而不是从零构造。

在不可变数据风格里,这几乎是日常操作。

MoonBit 写法

struct User {
  name : String
  age : Int
  city : String
}

fn clone_with_age(user : User, age : Int) -> User {
  { name: user.name, age, city: user.city }
}

fn clone_with_city(user : User, city : String) -> User {
  { name: user.name, age: user.age, city }
}

MoonBit 视角

Prototype 在 MoonBit 中经常不是一个“显式模式”,而是一种天然的数据演化方式:

  • 复制旧值
  • 修改局部字段
  • 得到新值

因此它特别适合:

  • 配置模板
  • 默认对象
  • 请求上下文复制
  • AST 节点变换

5. Singleton

意图

保证某个类只有一个实例,并提供全局访问点。

这是经典模式里最容易被滥用的一个。
在 MoonBit 中,很多 Singleton 的需求其实只是:

  • 一个模块级常量
  • 一个全局配置
  • 一个共享缓存
  • 一个进程级资源句柄

MoonBit 写法

struct Config {
  endpoint : String
}

let global_config = { endpoint: "https://api.example.com" }

fn get_config() -> Config {
  global_config
}

MoonBit 视角

Singleton 在 MoonBit 里不必神化。
绝大多数情况下你真正需要的只是:

  • 模块级唯一值
  • 显式依赖注入
  • 生命周期清晰的共享资源

如果可以显式传参,优先不要滥用全局单例


二、结构型模式


6. Adapter

意图

把一个接口转换成客户端期待的另一个接口。

当你接入第三方库、旧接口、遗留协议时,这个模式非常常见。

MoonBit 写法

struct LegacyUser {
  first_name : String
  last_name : String
}

struct UserView {
  full_name : String
}

fn adapt_user(u : LegacyUser) -> UserView {
  { full_name: u.first_name + " " + u.last_name }
}

函数适配器

fn parse_int_legacy(s : String) -> Int {
  // 假设旧接口
  42
}

fn parse_int_option(s : String) -> Option[Int] {
  Some(parse_int_legacy(s))
}

MoonBit 视角

Adapter 在 MoonBit 中本质是:

  • 数据转换函数
  • 包装 record
  • 接口重整模块

它特别常见于边界层

  • API 层
  • DB 层
  • 第三方 SDK 层
  • DTO / domain model 转换层

7. Bridge

意图

将抽象部分与实现部分分离,使它们可以独立变化。

传统 OO 会做成“抽象层次”和“实现层次”两套继承体系。
MoonBit 更自然的方式是:

  • 把实现能力放进 record
  • 把抽象逻辑写成独立函数
  • 在运行时组合

MoonBit 写法

struct Renderer {
  draw_circle : (Int) -> Unit
}

struct Circle {
  radius : Int
  renderer : Renderer
}

fn draw(circle : Circle) -> Unit {
  circle.renderer.draw_circle(circle.radius)
}

fn screen_renderer() -> Renderer {
  {
    draw_circle: fn(r) { println("draw circle on screen: \{r}") }
  }
}

fn svg_renderer() -> Renderer {
  {
    draw_circle: fn(r) { println("draw circle in svg: \{r}") }
  }
}

MoonBit 视角

Bridge 的重点不是类图,而是:

把“业务抽象”与“底层实现”拆开,然后通过组合连接。

这在 MoonBit 里会比继承更自然。


8. Composite

意图

把对象组织成树结构,并统一对待单个对象和组合对象。

这是 MoonBit 非常适合的模式之一,因为递归 enum 极其自然。

MoonBit 写法

enum Node {
  File(String, Int)
  Dir(String, Array[Node])
}

fn total_size(node : Node) -> Int {
  match node {
    File(_, size) => size
    Dir(_, children) => {
      let mut sum = 0
      for child in children {
        sum = sum + total_size(child)
      }
      sum
    }
  }
}

MoonBit 视角

Composite 在 MoonBit 中几乎可以直接翻译成:

  • 递归数据结构
  • 递归处理函数

典型场景:

  • 文件系统
  • AST
  • UI 树
  • 表达式树
  • 组织结构树

9. Decorator

意图

在不改变对象结构的前提下,动态地给对象添加职责。

MoonBit 中常见的两种写法:

  • 包装函数
  • 包装 record 并转发调用

装饰函数

fn with_log(f : (Int) -> Int) -> (Int) -> Int {
  fn(x) {
    println("input: \{x}")
    let y = f(x)
    println("output: \{y}")
    y
  }
}

fn inc(x : Int) -> Int { x + 1 }

装饰服务对象

struct Service {
  run : (String) -> String
}

fn with_cache(service : Service) -> Service {
  {
    run: fn(input) {
      println("cache lookup: \{input}")
      service.run(input)
    }
  }
}

MoonBit 视角

Decorator 在 MoonBit 里非常接近:

  • middleware
  • higher-order function
  • wrapper service

比如日志、缓存、鉴权、指标上报,天然就是 decorator 风格。


10. Facade

意图

为复杂子系统提供统一的简单接口。

这个模式在 MoonBit 中完全可以只是一个模块入口函数。

MoonBit 写法

fn parse_config(path : String) -> String {
  "config-from-\{path}"
}

fn init_logger() -> Unit {
  println("logger initialized")
}

fn connect_db() -> Unit {
  println("db connected")
}

fn start_app(config_path : String) -> Unit {
  let _config = parse_config(config_path)
  init_logger()
  connect_db()
  println("app started")
}

MoonBit 视角

Facade 的核心不是隐藏所有细节,而是:

对外提供一个更稳定、更低认知负担的入口。

这对于公共 API、SDK、模块首页尤其重要。
而在参考文章中,Facade 也被概括为 single point of access。(mishadoff.com)


11. Flyweight

意图

共享细粒度对象,减少内存使用。

关键点在于区分:

  • 内部状态:可共享
  • 外部状态:由使用方传入

MoonBit 写法

struct Glyph {
  ch : Char
}

struct GlyphFactory {
  mut pool : Map[Char, Glyph]
}

fn new_factory() -> GlyphFactory {
  { pool: {} }
}

fn get_glyph(factory : GlyphFactory, ch : Char) -> Glyph {
  match factory.pool.get(ch) {
    Some(g) => g
    None => {
      let g = { ch }
      factory.pool[ch] = g
      g
    }
  }
}

fn draw(glyph : Glyph, x : Int, y : Int) -> Unit {
  println("draw \{glyph.ch} at (\{x}, \{y})")
}

MoonBit 视角

Flyweight 在 MoonBit 中通常表现为:

  • interning
  • symbol 池
  • token / glyph 复用
  • 配置模板共享

重点不是“对象模式”本身,而是共享策略


12. Proxy

意图

为其他对象提供一个替身或占位符,以控制访问。

Proxy 和 Decorator 很像,但重点不同:

  • Decorator 更强调“增强行为”
  • Proxy 更强调“控制访问”

MoonBit 写法

struct ImageLoader {
  load : () -> String
}

fn real_image(path : String) -> ImageLoader {
  {
    load: fn() { "loaded image from " + path }
  }
}

fn lazy_image(path : String) -> ImageLoader {
  let mut cached : Option[String] = None
  {
    load: fn() {
      match cached {
        Some(v) => v
        None => {
          let v = "loaded image from " + path
          cached = Some(v)
          v
        }
      }
    }
  }
}

MoonBit 视角

常见 Proxy 形式包括:

  • lazy proxy
  • remote proxy
  • access-control proxy
  • cache proxy

在 MoonBit 里,通常就是一个包装层加上访问控制逻辑。


三、行为型模式


13. Chain of Responsibility

意图

让多个处理者都有机会处理请求,请求沿链传递,直到被处理。

参考文章里把它概括为 function composition 的方向,这很符合函数式语言视角。(mishadoff.com)

MoonBit 写法

fn handle_number(s : String) -> Option[String] {
  if s == "number" { Some("handled by number handler") } else { None }
}

fn handle_string(s : String) -> Option[String] {
  if s == "string" { Some("handled by string handler") } else { None }
}

fn handle_default(_s : String) -> Option[String] {
  Some("handled by default handler")
}

fn dispatch(
  req : String,
  handlers : Array[(String) -> Option[String]]
) -> String {
  for h in handlers {
    match h(req) {
      Some(v) => return v
      None => ()
    }
  }
  "unhandled"
}

MoonBit 视角

这是 Web middleware、编译管线、规则系统里最常见的模式之一。
本质就是:

  • 处理器数组
  • 顺序尝试
  • 命中即返回

14. Command

意图

把请求封装成对象,从而支持排队、记录、撤销、重做等能力。

在 MoonBit 中,Command 常见两种形态:

  • 函数值
  • enum 命令值

MoonBit 写法:命令即数据

enum Command {
  Save(String)
  Delete(String)
  Print(String)
}

fn execute(cmd : Command) -> Unit {
  match cmd {
    Save(path) => println("save: \{path}")
    Delete(path) => println("delete: \{path}")
    Print(msg) => println(msg)
  }
}

命令即函数

fn run_all(cmds : Array[() -> Unit]) -> Unit {
  for cmd in cmds {
    cmd()
  }
}

MoonBit 视角

如果你要:

  • 记录日志
  • 事件回放
  • 撤销/重做
  • 序列化命令

那么 enum Command 更适合。
如果只关心“可执行”,直接用函数更简单。


15. Interpreter

意图

为一种语言定义表示法,并提供解释器。

参考文章中把 Interpreter 概括为 “一组处理树的函数”,这几乎就是 MoonBit 最自然的写法。(mishadoff.com)

MoonBit 写法

enum Expr {
  Lit(Int)
  Add(Expr, Expr)
  Sub(Expr, Expr)
  Mul(Expr, Expr)
}

fn eval(expr : Expr) -> Int {
  match expr {
    Lit(n) => n
    Add(a, b) => eval(a) + eval(b)
    Sub(a, b) => eval(a) - eval(b)
    Mul(a, b) => eval(a) * eval(b)
  }
}

带环境的版本

enum Expr2 {
  Lit2(Int)
  Var(String)
  Add2(Expr2, Expr2)
}

type Env = Map[String, Int]

fn eval2(expr : Expr2, env : Env) -> Int {
  match expr {
    Lit2(n) => n
    Var(name) => env[name]
    Add2(a, b) => eval2(a, env) + eval2(b, env)
  }
}

MoonBit 视角

Interpreter 在 MoonBit 里特别“原生”:

  • enum 定义语法树
  • match 定义语义
  • 用递归定义求值规则

做 DSL、表达式系统、规则引擎时非常顺手。


16. Iterator

意图

提供一种顺序访问聚合对象中元素的方法,而不暴露底层结构。

很多时候 MoonBit 自身的数据结构和循环就已经足够。
只有在需要懒遍历统一迭代协议隐藏内部结构时,才需要显式 Iterator。

MoonBit 写法

enum IterResult[A] {
  Done
  Yield(A)
}

struct CounterIter {
  mut current : Int
  end_ : Int
}

fn next(iter : CounterIter) -> IterResult[Int] {
  if iter.current >= iter.end_ {
    Done
  } else {
    let v = iter.current
    iter.current = iter.current + 1
    Yield(v)
  }
}

MoonBit 视角

Iterator 在 MoonBit 中的存在感通常不如 OO 语言强,
因为很多遍历需求已经被:

  • for
  • 数组
  • 列表处理
  • 高阶函数

天然消化掉了。


17. Mediator

意图

用一个中介对象来封装一系列对象之间的交互,从而减少对象之间的直接耦合。

MoonBit 写法

enum Msg {
  FromUser(String)
  FromSystem(String)
}

struct ChatMediator {
  notify : (Msg) -> Unit
}

fn make_mediator() -> ChatMediator {
  {
    notify: fn(msg) {
      match msg {
        FromUser(s) => println("system got user msg: \{s}")
        FromSystem(s) => println("user got system msg: \{s}")
      }
    },
  }
}

MoonBit 视角

Mediator 在 MoonBit 中通常表现为:

  • 调度器
  • 协调器
  • 事件中心
  • service orchestrator

一旦多个组件互相调用形成网状结构,就适合引入 mediator 收敛依赖。


18. Memento

意图

在不破坏封装的前提下,捕获并保存对象内部状态,以便之后恢复。

参考文章对 Memento 的解释也强调:
需要保存状态的主体、负责保存的人,以及代表快照的对象。(mishadoff.com)

MoonBit 对这个模式很友好,因为不可变值天生适合做快照。

MoonBit 写法

struct Editor {
  text : String
}

fn save(editor : Editor) -> Editor {
  editor
}

fn restore(_editor : Editor, snapshot : Editor) -> Editor {
  snapshot
}

fn append(editor : Editor, s : String) -> Editor {
  { text: editor.text + s }
}

MoonBit 视角

Memento 在 MoonBit 里很自然,因为:

  • 值即快照
  • 复制便宜
  • 回滚语义清晰

特别适合:

  • 编辑器
  • 撤销/重做
  • 配置回滚
  • 状态机历史记录

19. Observer

意图

定义对象之间的一对多依赖,当一个对象状态变化时,所有依赖者都会收到通知。

参考文章里把 Observer 概括成“在另一个函数之后调用的函数”,也就是回调式通知。(mishadoff.com)

MoonBit 写法

struct Observable[A] {
  mut value : A
  mut observers : Array[(A) -> Unit]
}

fn new_observable[A](value : A) -> Observable[A] {
  { value, observers: [] }
}

fn subscribe[A](o : Observable[A], f : (A) -> Unit) -> Unit {
  o.observers.push(f)
}

fn set[A](o : Observable[A], value : A) -> Unit {
  o.value = value
  for f in o.observers {
    f(value)
  }
}

MoonBit 视角

Observer 是 GUI、状态同步、事件系统的经典模式。
在 MoonBit 里它最自然的形态就是:

  • 回调列表
  • 事件监听器
  • 简单 pub-sub

20. State

意图

允许对象在内部状态改变时改变其行为,看起来像是改变了类。

MoonBit 中这几乎总是写成有限状态机。

MoonBit 写法

enum DoorState {
  Open
  Closed
  Locked
}

enum Event {
  OpenDoor
  CloseDoor
  Lock
  Unlock
}

fn transit(state : DoorState, event : Event) -> DoorState {
  match (state, event) {
    (Closed, OpenDoor) => Open
    (Open, CloseDoor) => Closed
    (Closed, Lock) => Locked
    (Locked, Unlock) => Closed
    _ => state
  }
}

MoonBit 视角

State 在 MoonBit 里通常不需要“状态类”:

状态就是数据,行为就是匹配状态后的函数。

这是 MoonBit 对这个模式最漂亮的地方之一。


21. Strategy

意图

定义一系列算法,把它们封装起来,并使它们可以互换。

在函数式语言里,这几乎就是“传函数”。
参考文章也明确把 Clojure 里的 Strategy 视作函数式解决方案的典型例子。(mishadoff.com)

MoonBit 写法

fn sort_with(
  xs : Array[Int],
  less : (Int, Int) -> Bool
) -> Array[Int] {
  let ys = xs.copy()
  let n = ys.length()
  for i = 0; i < n; i = i + 1 {
    for j = i + 1; j < n; j = j + 1 {
      if less(ys[j], ys[i]) {
        let tmp = ys[i]
        ys[i] = ys[j]
        ys[j] = tmp
      }
    }
  }
  ys
}

fn asc(a : Int, b : Int) -> Bool { a < b }
fn desc(a : Int, b : Int) -> Bool { a > b }

MoonBit 视角

在 MoonBit 中,Strategy 很少需要显式接口。
当你发现“这里只是想替换一段算法”时,先问自己一句:

能不能直接传函数?

通常答案是:可以。


22. Template Method

意图

定义一个算法骨架,把一些步骤延迟到子类实现。

参考文章甚至把 Template Method 直接总结为 strategy with defaults,这个理解特别适合函数式语境。(mishadoff.com)

MoonBit 写法

fn process(
  input : String,
  validate : (String) -> Bool,
  transform : (String) -> String,
  output : (String) -> Unit
) -> Unit {
  if validate(input) {
    let result = transform(input)
    output(result)
  } else {
    println("invalid input")
  }
}

MoonBit 视角

在 MoonBit 中,Template Method 一般会退化为:

  • 一个固定骨架函数
  • 若干可替换步骤函数
  • 默认实现作为普通函数参数传入

所以它和 Strategy 的边界在函数式语言里会变得很近。


23. Visitor

意图

表示一个作用于某对象结构中的各元素的操作,使你可以在不改变元素类的前提下定义新的操作。

在传统 OO 里,Visitor 是“为了解决新增操作难”的经典技巧。
但在 MoonBit 中,由于 match 非常强,很多 Visitor 需求会被直接吸收。

MoonBit 写法:更自然的版本

enum Expr {
  Num(Int)
  Add(Expr, Expr)
  Mul(Expr, Expr)
}

fn eval(expr : Expr) -> Int {
  match expr {
    Num(n) => n
    Add(a, b) => eval(a) + eval(b)
    Mul(a, b) => eval(a) * eval(b)
  }
}

fn pretty(expr : Expr) -> String {
  match expr {
    Num(n) => n.to_string()
    Add(a, b) => "(\{pretty(a)} + \{pretty(b)})"
    Mul(a, b) => "(\{pretty(a)} * \{pretty(b)})"
  }
}

如果真的想显式 Visitor

struct ExprVisitor[A] {
  visit_num : (Int) -> A
  visit_add : (Expr, Expr) -> A
  visit_mul : (Expr, Expr) -> A
}

fn visit[A](expr : Expr, v : ExprVisitor[A]) -> A {
  match expr {
    Num(n) => v.visit_num(n)
    Add(a, b) => v.visit_add(a, b)
    Mul(a, b) => v.visit_mul(a, b)
  }
}

MoonBit 视角

Visitor 在 MoonBit 里最大的变化是:

  • 如果数据结构稳定、操作经常增加:Visitor 仍有价值
  • 如果只是普通递归处理:match 往往更直接

哪些模式在 MoonBit 里最“原生”?

如果只从“语言契合度”来看,下面这些模式在 MoonBit 中特别自然:

最自然的一组

  • Strategy
  • State
  • Interpreter
  • Composite
  • Command
  • Facade
  • Observer
  • Factory Method

原因很简单:

  • 有函数一等公民
  • 有代数数据类型
  • 有模式匹配
  • 有清晰模块边界

哪些模式在 MoonBit 里会明显“变轻”?

有些模式在 OO 里很重,但在 MoonBit 中会被语言特性压缩:

典型变轻模式

  • Visitor → 变成 match
  • Template Method → 变成骨架函数 + 注入步骤
  • Strategy → 变成传函数
  • Prototype → 变成复制 record
  • Singleton → 变成模块级唯一值
  • Iterator → 变成普通遍历接口
  • Factory Method → 变成普通工厂函数

所以很多时候,不是模式没了,而是:

语言已经把这部分样板工作做掉了。


哪些模式在 MoonBit 中仍然非常有用?

另外一些模式即便到了 MoonBit 里,仍然非常重要:

仍然强有生命力的一组

  • Abstract Factory
  • Builder
  • Facade
  • Adapter
  • Decorator
  • Proxy
  • Chain of Responsibility
  • Mediator
  • Memento
  • Flyweight

因为这些模式解决的问题并不是语法问题,而是架构边界问题

  • 怎么创建一组兼容对象
  • 怎么减少耦合
  • 怎么包装能力
  • 怎么控制访问
  • 怎么统一入口
  • 怎么共享资源
  • 怎么保留历史状态

一个更实用的 MoonBit 设计模式速查表

如果你在写 MoonBit 时遇到下面这些需求,可以这样联想:

你遇到的问题 可以想到的模式 MoonBit 常见写法
想切换算法 Strategy 传函数
想表达状态流转 State enum + match
想做表达式解释器 Interpreter AST + eval
想构建树结构 Composite 递归 enum
想统一复杂系统入口 Facade 门面函数 / 模块
想把操作当成值 Command enum 或函数值
想支持撤销回滚 Memento 快照值
想逐步构造复杂对象 Builder builder record
想共享细粒度对象 Flyweight pool/cache
想包一层日志缓存鉴权 Decorator 高阶函数 / wrapper
想控制访问或延迟加载 Proxy 包装对象
想适配旧接口 Adapter 转换函数
想解耦多方交互 Mediator 中央协调器
想一对多通知 Observer 回调列表
想顺序尝试多个处理器 Chain of Responsibility handlers 数组

设计模式不是模板,而是命名后的经验

我觉得这是学习设计模式时最重要的一点。

GoF 模式最有价值的地方,不是教你写出一套固定样板,而是提供一组可沟通的名字
这样当你和别人讨论设计时,可以很快说清楚:

  • “这里像个 Facade”
  • “这一层其实是 Adapter”
  • “这段规则链可以看成 Chain of Responsibility”
  • “这个 AST 的处理就是 Interpreter + Composite”
  • “这里不用硬上 Visitor,match 就够了”
  • “这个构造过程适合 Builder”

这也是为什么哪怕换到 MoonBit,设计模式依然有用。
因为模式解决的是设计问题,不是某门语言的语法题。


结语:用 MoonBit 学设计模式,重点是“去样板化”

如果把这篇文章压缩成一句话,我会这么说:

在 MoonBit 里学设计模式,不是把 OO 模式逐字翻译过来,而是把它们还原成“变化点组织方式”。

MoonBit 提供的优势在于:

  • enum 把状态和结构变成显式数据
  • match 把分派逻辑写清楚
  • 用函数把行为作为值传递
  • 用模块把边界收拢
  • 用不可变值让快照、复制、变换更自然

所以真正适合 MoonBit 的设计模式实践,不是更多继承,而是:

  • 更好的数据建模
  • 更好的函数抽象
  • 更清晰的模块边界
  • 更少的样板代码
  • 更明确的变化点隔离

而这,恰恰也是函数式与现代类型系统语言里“模式感”真正该落地的地方。


如果你愿意,我下一步可以继续帮你做两件事中的任意一种:

  1. 把这篇博客润色成更正式的中文技术博客发布版,加上更自然的小标题、导语、结尾和目录
  2. 把 23 个模式分别拆成独立小节的 Markdown 文档版,每个模式都补上“适用场景 / 优点 / 代价 / MoonBit 注意事项”
1 个赞

好思路,好文章. 期待继续, 建议官方(一起优化)发布到官方Blog.