桥接模式在Go中通过接口与结构体组合实现解耦,核心是定义小而专注的接口、用字段持有接口、避免实现间耦合,而非继承;警惕伪桥接和非正交维度的强行拆分。

桥接模式在 Go 里不靠继承,靠组合和接口
Go 没有类继承,所以传统 OOP 中的“桥接模式”不能照搬 UML 图那一套。它的核心不是“抽象与实现分离”,而是「行为契约(接口)与具体实现(结构体)解耦,再通过字段组合把实现注入抽象」。关键判断:如果你发现某个结构体里反复要写 if implType == "A" { ... } else if implType == "B" { ... },就该考虑桥接了。
定义稳定接口,让实现自由替换
接口要小、专注、只描述“做什么”,不约束“怎么做”。比如日志模块,不要定义 LogWithFileAndRotation(),而应拆成 Logger 和 Writer 两个接口:
type Logger interface {
Info(msg string)
Error(msg string)
}
type Writer interface {
Write([]byte) (int, error)
}
这样 ConsoleWriter、FileWriter、KafkaWriter 可以独立演进,不影响 Logger 的使用者。别把所有能力塞进一个大接口——它会迫使每个实现都处理自己根本不需要的逻辑。
用结构体字段持有接口,而非硬编码实现
桥接的“桥”就是结构体里的接口字段。例如:
立即学习“go语言免费学习笔记(深入)”;
type Service struct {
logger Logger
writer Writer
}
func NewService(l Logger, w Writer) *Service {
return &Service{logger: l, writer: w}
}
func (s *Service) DoWork() {
s.logger.Info("starting")
data := []byte("payload")
s.writer.Write(data) // 具体写哪,由注入的 Writer 决定
}
- 构造函数接收接口,不 new 具体类型
- 避免在
Service内部 import 实现包(如_ "myapp/writer/file") - 测试时可直接传入
&mockWriter{},不用改任何业务逻辑
警惕“伪桥接”:接口只是装饰,没真正解耦
常见错误是定义了接口,但实现体仍强依赖具体类型,或接口方法内部又 switch 实现名。比如:
// ❌ 错误示范:接口没起作用
type BadLogger interface {
Log(level string, msg string)
}
// 实现里还是 if level == "file" ...
func (l *BadLoggerImpl) Log(level string, msg string) {
switch level {
case "file": writeToDisk(msg)
case "console": fmt.Println(msg)
}
}
这本质是策略模式的劣化版,不是桥接。真正的桥接要求:接口方法不感知实现细节,实现体不感知其他实现存在。一旦你发现自己在接口方法里做类型断言(if w, ok := writer.(*FileWriter)),说明桥已断裂。
桥接最难的部分不是写代码,而是识别哪些变化维度真正正交——比如“日志级别”和“输出目标”是正交的,但“日志格式”和“输出目标”往往耦合紧密,强行桥接反而增加维护成本。










