bridge比直接调用exporttocsv(data)+exporttojson(data)更可靠,因其用datasource和formatter双接口解耦数据获取与格式输出,避免修改频繁导致的代码散乱和重复;formatter必须接收interface{}以支持多类型复用,且需在exporter中加入标准化转换或类型校验防隐式耦合;大文件导出时应扩展流式接口避免oom。

为什么 Bridge 比直接写 ExportToCSV(data) + ExportToJSON(data) 更可靠
因为数据获取逻辑(比如从数据库查用户、加缓存、做分页)和导出格式(CSV/JSON/Excel)天然变化频率不同。硬编码耦合会导致每次新增一种格式,就得改所有数据源函数;加个字段过滤条件,又得同步修五六个导出函数。
桥接模式用两个独立接口解耦:一个管「怎么拿数据」,一个管「怎么写出去」。中间靠一个统一的 Exporter 接口桥接,不互相依赖。
-
DataSource接口只定义GetData(),不管输出长什么样 -
Formatter接口只定义Format(data interface{}) ([]byte, error),不关心数据从哪来 -
Exporter结构体持有一个DataSource和一个Formatter,组合调用即可
示例:
type Exporter struct {
source DataSource
format Formatter
}
func (e *Exporter) Export() ([]byte, error) {
data := e.source.GetData()
return e.format.Format(data)
}
Go 里实现桥接时最容易漏掉的接口抽象点
新手常把 Formatter 设计成接收具体结构体(如 func Format(users []User)),这会让每种格式器都绑定死某类数据,失去复用性。
必须让 Formatter.Format() 接收 interface{},再由具体格式器内部做类型断言或反射处理——否则加个 Product 导出,就得重写全部格式器。
立即学习“go语言免费学习笔记(深入)”;
- CSV 格式器可接受
[]map[string]interface{}或[][]string,但入口仍是interface{} - JSON 格式器直接传给
json.Marshal,对输入类型宽容度高 - Excel(如用
tealeg/xlsx)需转成二维切片,这部分转换逻辑应放在格式器内部,而非暴露给DataSource
错误示范:func (f *CSVFormatter) Format(users []User) → 正确应为 func (f *CSVFormatter) Format(data interface{})
如何避免 DataSource 和 Formatter 之间隐式耦合
看似解耦了,但实际运行时仍可能崩:比如 JSONFormatter 期望输入是 struct,而某个 DBSource 返回的是 map[string]interface{},虽能 Marshal,但字段名大小写、omitempty 行为全靠碰运气。
真正可控的做法,是在 Exporter.Export() 中加一层轻量校验或标准化转换:
- 约定所有
DataSource.GetData()返回值实现Exportable接口(含ToMapSlice() []map[string]interface{}方法) - 或让
Formatter实现Accepts(dataType string) bool,在运行时检查兼容性(如"[]User"vs"[]map[string]interface{}") - 不推荐用
reflect.TypeOf在每次导出时做深度判断——性能敏感场景下,这会成为瓶颈
典型报错:json: unsupported type: map[interface {}]interface{},往往就因 DataSource 返回了未规范化的嵌套 map
导出大文件时,Bridge 结构下的内存与流式处理陷阱
桥接模式默认走内存中转:先 GetData() 拿全量,再 Format() 序列化。对百万行 CSV 或百 MB JSON,极易 OOM。
解决方式不是放弃桥接,而是把「流式能力」下沉到接口契约里:
- 扩展
Formatter接口,增加FormatStream(writer io.Writer, dataSource DataSource) error -
DataSource需支持游标式读取(如返回func() (interface{}, bool)迭代器) -
Exporter提供ExportStream(io.Writer)方法,跳过内存聚合阶段
注意:json.Encoder 支持流式写入,但 CSV(如 encoding/csv)也得配合 csv.Writer.WriteAll 分批调用,不能指望单次 Format() 自动拆流










