Go接口通过约定、组合与隐式实现自然承载设计模式:io.Reader体现策略模式,嵌入接口实现装饰器,context.Context简化观察者,结构体直接实现多接口完成适配。

Go 语言本身没有类、继承或抽象类,但接口(interface{})是体现设计模式思想最自然、最轻量的载体。它不靠语法糖,而靠「约定 + 组合 + 隐式实现」来落地常见模式。
用 io.Reader 和 io.Writer 理解策略模式
策略模式的核心是「封装可互换的算法」。Go 标准库的 io.Reader 就是典型:任何实现了 Read([]byte) (int, error) 方法的类型,都自动成为一种读取策略——文件、网络连接、内存字节切片、gzip 流,全都可以无缝替换。
实操建议:
技术上面应用了三层结构,AJAX框架,URL重写等基础的开发。并用了动软的代码生成器及数据访问类,加进了一些自己用到的小功能,算是整理了一些自己的操作类。系统设计上面说不出用什么模式,大体设计是后台分两级分类,设置好一级之后,再设置二级并选择栏目类型,如内容,列表,上传文件,新窗口等。这样就可以生成无限多个二级分类,也就是网站栏目。对于扩展性来说,如果有新的需求可以直接加一个栏目类型并新加功能操作
- 定义小而专注的接口(如只含一个方法),比大而全的接口更容易被复用和测试
- 避免在接口中暴露实现细节(例如不要加
Close()到Reader,那是io.Closer的事) - 策略切换发生在调用方,而非实现内部——比如
json.NewDecoder(r io.Reader)不关心r是来自磁盘还是 HTTP body
通过组合隐式实现装饰器模式
Go 没有 extends,但你可以把一个接口字段嵌入结构体,再选择性地重写部分方法——这就是装饰器。比如 http.Transport 包裹底层连接,bufio.Reader 包裹另一个 io.Reader。
立即学习“go语言免费学习笔记(深入)”;
常见错误现象:直接复制原接口方法逻辑,却忘了调用被包装对象的对应方法,导致装饰失效。
实操建议:
- 装饰器结构体必须持有被装饰的接口值(如
reader io.Reader) - 重写方法时,多数逻辑应委托给
r.reader.Read(...),仅在前后添加额外行为(日志、限速、重试) - 不要试图让装饰器“实现更多接口”,除非业务强依赖——否则会破坏单一职责
context.Context 是观察者模式的极简实现
它不显式注册监听器,而是通过派生新 Context(如 WithCancel、WithTimeout)并传递给下游函数,让所有参与方「被动响应」取消信号。这比手动维护回调列表更符合 Go 的并发哲学。
使用场景:
- HTTP handler 中传入
ctx,下游 DB 查询、RPC 调用都基于它做超时控制 - 协程之间不共享状态,只共享一个可监听的
Done()channel
容易踩的坑:
- 把
context.Background()或context.TODO()直接传进长期运行的 goroutine,导致无法取消 - 在函数参数里混用自定义取消 channel 和
context.Context,破坏统一信号源
接口嵌套与类型断言体现适配器模式
当第三方库返回一个结构体(如 net/http.Response),而你需要把它当作某个接口(如 io.ReadCloser)使用时,Go 不需要显式写 HttpToIoAdapter 类——因为 *http.Response 本身就实现了 io.ReadCloser(它同时满足 Read() 和 Close())。
实操建议:
- 优先让结构体实现多个小接口,而不是塞进一个大接口(例如
io.ReadWriteCloser是三个接口的组合) - 类型断言
v, ok := x.(MyInterface)是运行时适配检查,不是强制转换;失败时ok == false,别忽略ok - 避免深度嵌套接口(如
type A interface{ B }→type B interface{ C }),增加理解成本和 mock 难度
Go 接口的价值不在语法多强大,而在它迫使你提前思考「谁用这个行为」「这个行为是否稳定」「要不要拆开」。很多设计模式的复杂性,在 Go 里被压缩成一行接口声明和一个方法签名——但这也意味着,一旦接口定型,修改成本极高。









