Go项目少提设计模式因其语言特性天然抑制OOP模板化写法;高频落地的是策略模式(函数类型/接口)、选项模式(Option函数)和装饰器模式(包装函数),且均以简洁惯用法实现。
因为 Go 的语言特性(无类、无继承、接口隐式实现、组合优先)天然抑制了传统 OOP 设计模式的“模板化写法”。factory、singleton、observer 这些词在 Go 代码里几乎不作为包名或结构体前缀出现——它们被拆解成更轻量、更贴近问题域的实现。比如,你不会写一个 MySQLConnectionSingleton,而是用 var db *sql.DB 全局变量 + init() 初始化,再配合 sync.Once 控制;这不是绕过模式,而是用语言原语直接达成相同目的。
实际项目中,以下三种模式出现频率最高,且都有明确、简洁的 Go 实现惯用法:
type Middleware func(http.Handler) http.Handler,每个中间件是独立函数,组合靠闭包或切片遍历,无需 Strategy 接口和一堆实现类。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 的服务:
func TestPaymentService_Process(t *testing.T) {
svc := NewPaymentService(&mockHTTPClient{})
// ...
}
这时你自然会提取 type HTTPDoer interface { Do(*http.Request) (*http.Response, error) },而不是先设计“适配器模式”。模式在这里是副产品,不是起点。真正卡住落地的,从来不是“该不该用”,而是“这个接口要不要导出”“这个结构体字段要不要公开”“这个错误类型该定义成自定义 error 还是直接用 fmt.Errorf”——这些细节比模式名称重
