对比学习用, 没有详细核对.
- 参考 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 个模式分为三类:
创建型
- Abstract Factory
- Builder
- Factory Method
- Prototype
- Singleton
结构型
- Adapter
- Bridge
- Composite
- Decorator
- Facade
- Flyweight
- Proxy
行为型
- Chain of Responsibility
- Command
- Interpreter
- Iterator
- Mediator
- Memento
- Observer
- State
- Strategy
- Template Method
- 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 的设计模式实践,不是更多继承,而是:
- 更好的数据建模
- 更好的函数抽象
- 更清晰的模块边界
- 更少的样板代码
- 更明确的变化点隔离
而这,恰恰也是函数式与现代类型系统语言里“模式感”真正该落地的地方。
如果你愿意,我下一步可以继续帮你做两件事中的任意一种:
- 把这篇博客润色成更正式的中文技术博客发布版,加上更自然的小标题、导语、结尾和目录
- 把 23 个模式分别拆成独立小节的 Markdown 文档版,每个模式都补上“适用场景 / 优点 / 代价 / MoonBit 注意事项”