Go项目少提设计模式因其语言特性天然抑制OOP模板化写法;高频落地的是策略模式(函数类型/接口)、选项模式(Option函数)和装饰器模式(包装函数),且均以简洁惯用法实现。

为什么 Go 项目里很少显式提“设计模式”
因为 Go 的语言特性(无类、无继承、接口隐式实现、组合优先)天然抑制了传统 OOP 设计模式的“模板化写法”。factory、singleton、observer 这些词在 Go 代码里几乎不作为包名或结构体前缀出现——它们被拆解成更轻量、更贴近问题域的实现。比如,你不会写一个 MySQLConnectionSingleton,而是用 var db *sql.DB 全局变量 + init() 初始化,再配合 sync.Once 控制;这不是绕过模式,而是用语言原语直接达成相同目的。
真正高频落地的三个模式及对应 Go 写法
实际项目中,以下三种模式出现频率最高,且都有明确、简洁的 Go 实现惯用法:
-
策略模式:用函数类型或接口抽象算法变体。例如 HTTP 中间件链:
type Middleware func(http.Handler) http.Handler,每个中间件是独立函数,组合靠闭包或切片遍历,无需Strategy接口和一堆实现类。 -
选项模式(Options Pattern):替代构造函数参数爆炸。典型如
grpc.Dial(..., grpc.WithTimeout(...), grpc.WithBlock())。核心是定义type Option func(*Client),每个选项函数修改接收者字段,构造时用可变参数收集并依次调用。 -
装饰器模式:常用于日志、重试、熔断等横切逻辑。不是套用
type Decorator struct{ inner Service },而是用包装函数:比如func WithRetry(f Func) Func { ... },返回新函数,干净无状态。
避免过度抽象的两个信号
当团队开始为模式而模式时,Go 项目会迅速变得难维护。注意这两个危险信号:
- 接口定义远超实际调用方需要,比如定义
type Storer interface { Get(); Put(); Delete(); List(); Count(); HealthCheck() },但 90% 的调用方只用Get和Put—— 这违反了接口隔离原则,也增加 mock 成本。 - 为了“统一”把不同领域行为硬塞进同一接口,比如让
Cache、DB、HTTPClient都实现Executor接口,结果每个方法都要写panic("not implemented")或空分支 —— 这不是抽象,是耦合。
测试驱动下的模式选择更自然
Go 项目里最可靠的模式落地路径,往往始于测试需求。比如要测一个依赖外部 API 的服务:
立即学习“go语言免费学习笔记(深入)”;
func TestPaymentService_Process(t *testing.T) {
svc := NewPaymentService(&mockHTTPClient{})
// ...
}
这时你自然会提取 type HTTPDoer interface { Do(*http.Request) (*http.Response, error) },而不是先设计“适配器模式”。模式在这里是副产品,不是起点。真正卡住落地的,从来不是“该不该用”,而是“这个接口要不要导出”“这个结构体字段要不要公开”“这个错误类型该定义成自定义 error 还是直接用 fmt.Errorf”——这些细节比模式名称重要得多。










