一、创建型
关注如何创建对象:把“怎么 new”与“怎么用”解耦,让系统不依赖具体类型。
1. 单例 Singleton
思想:保证一个类只有一个实例,并提供全局访问点。逻辑:包内私有变量 + 受控获取入口 + 并发安全的一次性初始化。代价是隐含全局状态、不利测试,能用依赖注入时优先注入。
场景:全局配置;日志器;数据库连接池(sql.DB 本身即单例使用)。
var once sync.Once
var cfg *Config
func GetConfig() *Config { once.Do(func() { cfg = loadConfig() }); return cfg }
2. 工厂方法 Factory Method
思想:把“创建哪种对象”这个变化点封装起来,调用方只依赖接口,不直接 new 具体类型。逻辑:定义创建接口,由具体工厂决定产出,新增类型无需改调用方(开闭原则)。
场景:按渠道创建通知器(短信/邮件/推送);按文件类型创建解析器;日志输出目标创建。
type Notifier interface{ Send(msg string) }
type Creator interface{ Create() Notifier }
type SMSCreator struct{}
func (SMSCreator) Create() Notifier { return &SMS{} }
3. 抽象工厂 Abstract Factory
思想:一次创建“一整族”相关对象,保证它们互相兼容(不会把 S3 存储和阿里云队列混用)。逻辑:工厂接口含多个创建方法,切换工厂即整族切换。
场景:多云适配(AWS/阿里云/GCP 的存储+队列成套创建);多数据库方言;UI 主题套件。
type CloudFactory interface { NewStorage() Storage; NewQueue() Queue }
type AWSFactory struct{}
func (AWSFactory) NewStorage() Storage { return &S3{} }
func (AWSFactory) NewQueue() Queue { return &SQS{} }
4. 建造者 Builder
思想:把复杂对象的构造过程拆成多步,避免超长参数列表和非法中间状态。逻辑:链式设置属性,最后 Build 统一校验。Go 中也常用 Functional Options 达到同样效果。
场景:SQL 查询构造(squirrel、GORM 链式);构造 HTTP 请求;复杂订单/报表对象。
q := NewQuery().Table("users").Where("age > ?", 18).Limit(10).Build()
5. 原型 Prototype
思想:通过复制现有对象来创建新对象,而不是从头构造。逻辑:对象自带 Clone 方法。关键是区分浅拷贝与深拷贝,切片、map、指针字段要单独复制。
场景:克隆配置模板;游戏怪物/道具模板;文档模板复制。
func (c *Config) Clone() *Config {
n := *c
n.Tags = append([]string(nil), c.Tags...) // 深拷贝切片
return &n
}
二、结构型
关注如何组合类与对象:通过组合、包装,构成更大、更灵活的结构。
6. 适配器 Adapter
思想:转换接口,让原本不兼容的类能一起工作。逻辑:新建一层包装,对外实现调用方期望的接口,对内调用被适配者,不修改原有代码。
场景:旧支付接口适配新接口;第三方 SDK 统一成内部接口;http.HandlerFunc 把函数适配为 Handler。
type OldPay struct{}
func (OldPay) MakePayment(amt float64) {}
type PayAdapter struct{ old OldPay }
func (a PayAdapter) Pay(cents int) { a.old.MakePayment(float64(cents) / 100) }
7. 桥接 Bridge
思想:把抽象和实现分离,让两个维度各自独立变化。逻辑:用组合代替继承,将 M×N 个子类降为 M+N 个,避免类爆炸。
场景:通知类型 × 发送渠道两维度独立扩展;形状 × 渲染引擎;database/sql 抽象与驱动分离。
type Sender interface{ Send(string) } // 实现维度
type Alert struct{ s Sender } // 抽象维度
func (a Alert) Notify(m string) { a.s.Send("[ALERT] " + m) }
8. 组合 Composite
思想:用统一接口对待“单个对象”和“对象集合”,形成树形结构。逻辑:叶子和容器实现同一接口,容器递归调用子节点,调用方无需区分。
场景:文件系统目录树;组织架构/权限菜单树;UI 组件树。
type Node interface{ Size() int }
type Dir struct{ children []Node }
func (d Dir) Size() (s int) { for _, c := range d.children { s += c.Size() }; return }
9. 装饰器 Decorator
思想:不修改原对象,动态叠加额外职责。逻辑:装饰器与被装饰对象实现同一接口,内部持有被装饰对象,像洋葱一样层层包裹,用组合代替继承来扩展功能。
场景:HTTP 中间件(日志、鉴权、限流);gRPC 拦截器;io.Reader 包装(gzip、bufio)。
func Logging(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Println(r.URL); next.ServeHTTP(w, r)
})
}
10. 外观 Facade
思想:为复杂子系统提供一个简化的统一入口,降低调用方与子系统的耦合(最少知识原则)。逻辑:入口负责编排子系统调用;不阻止高级用户直接使用子系统。
场景:下单一个入口封装库存、支付、物流;对复杂 SDK 提供简化 API;启动器统一初始化各模块。
type OrderFacade struct{ inv Inventory; pay Payment; ship Shipping }
func (f OrderFacade) Place(o Order) { f.inv.Lock(o); f.pay.Charge(o); f.ship.Create(o) }
11. 享元 Flyweight
思想:共享大量细粒度对象中不变的“内部状态”,可变的“外部状态”由调用方传入,以节省内存和创建开销。逻辑:用工厂/池管理并复用共享对象。
场景:sync.Pool 对象复用;字符串驻留;游戏中大量相同粒子/地图瓦片共享外形数据。
var tiles = map[string]*Tile{} // 内部状态共享
func GetTile(kind string) *Tile { if t, ok := tiles[kind]; ok { return t }; t := newTile(kind); tiles[kind] = t; return t }
12. 代理 Proxy
思想:用代理对象控制对真实对象的访问。逻辑:代理与真实对象同接口,在转发调用前后插入缓存、鉴权、限流、懒加载等逻辑。与装饰器的区别:代理侧重“控制访问”,装饰器侧重“增强功能”。
场景:缓存代理;权限/限流代理;RPC 客户端 stub;图片/数据懒加载。
type CacheProxy struct{ real Repo; cache map[int]User }
func (p *CacheProxy) Get(id int) User {
if u, ok := p.cache[id]; ok { return u }
u := p.real.Get(id); p.cache[id] = u; return u
}
三、行为型
关注对象之间如何协作与分配职责:让交互松耦合、算法可替换。
13. 责任链 Chain of Responsibility
思想:让请求沿着一条链传递,每个节点自行决定处理或交给下一个。逻辑:发送者与接收者解耦,链可以动态增删、调整顺序。
场景:审批流(组长→经理→总监);风控规则依次校验;日志级别过滤;中间件链。
type Handler interface{ Handle(req Req) bool; SetNext(Handler) }
// 每个节点:能处理就处理,否则 next.Handle(req)
14. 命令 Command
思想:把“请求”封装成对象,从而可以排队、记录日志、撤销。逻辑:将“做什么”(命令对象)与“谁来做、何时做”(调用者、接收者)解耦。
场景:异步任务队列;编辑器撤销/重做;操作日志回放;CLI 子命令(cobra)。
type Command interface{ Execute(); Undo() }
history := []Command{}
cmd.Execute(); history = append(history, cmd)
history[len(history)-1].Undo() // 撤销
15. 解释器 Interpreter
思想:为一种简单语言定义文法,并用对象树表示、递归求值。逻辑:每条文法规则对应一个表达式类型。只适合小型 DSL,复杂语法应使用解析器生成工具。
场景:规则引擎(age>18 AND vip==true);搜索/过滤语法;模板与表达式求值。
type Expr interface{ Eval(ctx map[string]any) bool }
type And struct{ L, R Expr }
func (a And) Eval(c map[string]any) bool { return a.L.Eval(c) && a.R.Eval(c) }
16. 迭代器 Iterator
思想:提供顺序访问集合元素的统一方式,而不暴露集合内部结构。逻辑:把“遍历”从集合中抽离,调用方只关心“下一个元素”。
场景:分页游标遍历数据库/对象存储(S3 ListObjects);bufio.Scanner;Go 1.23 iter.Seq。
func (p *Pager) Items() iter.Seq[Item] {
return func(yield func(Item) bool) {
for p.HasNext() { for _, it := range p.NextPage() { if !yield(it) { return } } }
}
}
17. 中介者 Mediator
思想:用中介对象封装一组对象之间的交互,把网状的多对多关系变成星形。逻辑:同事对象只与中介通信,互不直接引用,交互规则集中管理。
场景:聊天室(用户经房间转发);事件总线;UI 控件联动;空管/调度中心。
type Room struct{ users []*User }
func (r *Room) Broadcast(from *User, msg string) {
for _, u := range r.users { if u != from { u.Receive(msg) } }
}
18. 备忘录 Memento
思想:在不破坏封装的前提下,捕获对象的内部状态并保存在外部,以便日后恢复。逻辑:由原对象生成快照,由管理者保管,需要时交还给原对象。
场景:编辑器撤销;游戏存档;事务回滚前快照;表单草稿。
type Snapshot struct{ text string }
func (e *Editor) Save() Snapshot { return Snapshot{e.text} }
func (e *Editor) Restore(s Snapshot) { e.text = s.text }
19. 观察者 Observer
思想:定义一对多的依赖关系:主题状态变化时,自动通知所有订阅者。逻辑:主题维护订阅列表并广播,双方只依赖抽象,实现松耦合的发布/订阅。
场景:事件订阅/发布;Kubernetes informer/watch;配置热更新;订单状态推送。
type Subject struct{ subs []chan Event }
func (s *Subject) Publish(e Event) { for _, ch := range s.subs { ch <- e } }
20. 状态 State
思想:把与状态相关的行为封装进各个状态对象,对象状态改变时切换状态对象。逻辑:用多态取代大量 if/switch,让状态转换规则清晰可扩展。
场景:订单状态机(待付款→已付款→已发货);TCP 连接状态;工作流审批。
type State interface{ Next(o *Order) }
type Paid struct{}
func (Paid) Next(o *Order) { o.state = Shipped{} }
21. 策略 Strategy
思想:定义一组可互换的算法,各自封装成独立单元,运行时按需选择。逻辑:把“怎么做”从“做什么”中抽离,调用方依赖策略接口(Go 中常用函数类型)。
场景:多种支付方式;促销折扣算法;负载均衡(轮询/随机/加权);限流算法;sort.Slice 传比较函数。
type Discount func(price float64) float64
func Checkout(p float64, d Discount) float64 { return d(p) }
Checkout(100, func(p float64) float64 { return p * 0.8 })
22. 模板方法 Template Method
思想:在一个函数中固定算法骨架,把其中某些步骤留给外部实现。逻辑:流程不变、步骤可变(好莱坞原则:别调用我,我会调用你)。Go 无继承,用接口或函数字段注入步骤。
场景:数据导入/导出固定流程(读取→校验→处理→写入);爬虫流程;测试 setup/teardown。
type Steps struct{ Read func() []byte; Validate func([]byte) bool; Save func([]byte) }
func Run(s Steps) { d := s.Read(); if s.Validate(d) { s.Save(d) } } // 固定骨架,步骤可替换
23. 访问者 Visitor
思想:把作用于数据结构的操作从结构中分离出来,新增操作无需修改元素类。逻辑:元素提供 Accept,访问者提供针对各类型的 Visit(双分派)。适合结构稳定、操作多变的场景。
场景:AST 遍历(go/ast.Inspect,代码检查/生成);报表导出多格式;对文件树做统计/压缩等不同操作。
type Visitor interface{ VisitFile(*File); VisitDir(*Dir) }
type Element interface{ Accept(Visitor) }
func (f *File) Accept(v Visitor) { v.VisitFile(f) }
速查:Go 中最常见的几种
- 装饰器 / 责任链:HTTP、gRPC 中间件。
- 策略:函数类型,最轻量。
- 观察者:channel + goroutine。
- 单例:
sync.Once。 - 迭代器:
range与iter.Seq。