Go语言不鼓励生搬硬套GoF设计模式,因其缺乏类继承和虚函数,接口轻量隐式实现;常用函数替代工厂、chan/select替代观察者、函数类型替代策略、闭包组合替代装饰器;仅Adapter、Option等契合Go习惯的模式具实用价值。
不需要。Go 语言的惯用法(idiom)和设计哲学与传统面向对象语言差异很大,生搬硬套 GoF 模式往往导致代码臃肿、可读性下降,甚至引入不必要的抽象。
Go 没有类继承、没有虚函数、接口是隐式实现且极其轻量——这意味着:
Factory、Abstract Factory 常被 func NewXXX() *XXX 函数替代,更直接,无类型擦除开销Observer 很少用回调注册+通知链,而是用 chan + select 实现事件分发,天然支持并发安全Strategy 通常直接传入函数类型(如 func(int) error),而非定义接口+多个 struct 实现Decorator 在 HTTP 中常用 http.Handler 链式包装,但底层靠闭包和函数组合,不是嵌套 struct + 接口代理不是全盘否定,而是看是否契合 Go 的表达习惯:
Singleton:仅限全局配置/资源(如 log.Logger、sql.DB),但应避免隐藏依赖;优先用显式传参或依赖注入(如 fx、wire)Adapter:高频使用,例如把第三方库的错误类型转为项目统一的 errors.Is() 可识别格式,或把 io.Reader 封装成自定义结构体以附加日志逻辑Option(非 GoF 原生,但已成 Go 事实标准):用 func(*T) 类型构造选项
函数,比长参数列表或 builder struct 更简洁灵活典型反模式:
Template Method,结果多出 3 个接口、2 个空实现、1 个抽象基 structVisitor 遍历 AST 节点,却忽略 Go 的 switch v := node.(type) 类型断言足够清晰且无反射开销Unwrap() 和 Is(),但实际只有 1–2 处需要链式判断,其余纯属冗余func NewClient(opts ...ClientOption) *Client {
c := &Client{timeout: 30 * time.Second}
for _, opt := range opts {
opt(c)
}
return c
}
type ClientOption func(*Client)
func WithTimeout(d time.Duration) ClientOption {
return func(c *Client) { c.timeout = d }
}
func WithLogger(l log.Logger) ClientOption {
return func(c Client) { c.logger = l }
}
真正该花时间琢磨的,不是“这个场景该用哪个模式”,而是 “这个接口要不要导出”、“这个 error 是否值得用 fmt.Errorf("xxx: %w", err) 包裹”、“这个 chan 是该缓冲还是无缓冲”。模式只是副产品,清晰、简单、可测试才是 Go 代码的第一目标。