go 中践行 solid 需重构思维:用 struct(仅存数据)+ interface(使用方定义、1–3 方法)+ 组合 + 显式依赖替代继承;副作用操作须抽离,接口实现需语义兼容,扩展靠函数或新类型而非修改旧代码。

Go 语言没有 class、不支持继承、接口是隐式实现的——直接套用传统面向对象语境下的 SOLID 解释,八成会写出更难维护的代码。SOLID 在 Go 里不是“翻译题”,而是“重构思维”:用 struct + interface + 组合 + 显式依赖,把变更点切开、把扩展点留活、把耦合点推到边界。
struct 只存数据,别写 Save/Log/Hash 方法
这是最常踩的坑:一个 User struct 里塞了 Save()、HashPassword()、ValidateEmail()、LogActivity()……它已经不是数据载体,是“上帝类型”。一旦数据库换 PostgreSQL、密码算法要升级、日志要对接 Loki,你得改同一份代码,测试全挂。
- struct 字段全小写(私有),只暴露读取方法如
Title()、Questions() - 所有副作用操作(I/O、加密、网络、日志)必须抽到独立类型中,通过参数或字段注入
- 判断标准:如果这个方法调用了
db.Query、log.Printf、bcrypt.GenerateFromPassword,它就不该在 struct 里
接口定义在使用方包里,而不是 infra 或 transport
比如 domain 层需要保存问卷,就该在 domain/ 下定义 SurveyRepository 接口;infra 层的 mysql.SurveyRepo 去实现它。反过来——infra 定义接口、domain 去实现——等于让核心业务逻辑依赖基础设施,违背 DIP。
Go语言(Golang)1.26.0版本提供 Go 官方 Windows amd64 MSI 安装包下载入口,版本号 1.26.0,可用于旧项目维护、兼容性测试和指定版本开发环境配置。
- 接口方法数控制在 1–3 个,例如:
Save(*Survey) error、FindByID(int) (*Survey, error) - 接口名带领域动词,避免
SurveyInterface这种泛称;用SurveyStorer或SurveyFinder更准确 - 如果某个接口只被一个地方用,且未来无替换可能(比如本地内存缓存),硬加接口反而增加跳转成本
用函数替代继承实现 OCP,新增行为不碰老代码
Go 没有子类,但你可以让行为“可插拔”。比如导出问卷,不要写 switch dst { case "s3": ... case "oss": ... },而应定义 Exporter 接口,每种目标实现一个结构体:
type Exporter interface {
Export(*Survey) error
}
type S3Exporter struct{ client *s3.Client }
func (e *S3Exporter) Export(s *Survey) error { ... }
type OSSEporter struct{ client *oss.Client }
func (e *OSSEporter) Export(s *Survey) error { ... }
- 上层代码只依赖
Exporter,新增阿里云 OSS 支持?加个新 struct 实现接口即可 - 避免为“理论上可能扩展”提前抽象——只有真实需求出现时再拆,否则就是过度设计
- 函数也可以作为扩展点:
Process(s *Survey, after func(*Survey) error),比接口更轻量
里氏替换不是语法能过,是语义不崩
接口方法签名一致 ≠ 行为兼容。比如 Cache.Get(key string) (any, bool) 接口,一个内存实现返回 (val, true),另一个 Redis 实现却在 key 不存在时 panic ——这违反 LSP。调用方按接口文档写的逻辑会直接崩。
- LSP 的关键在文档契约:是否允许空值?超时怎么处理?错误是否可重试?这些必须写进接口注释
- 测试要覆盖“替换后行为一致”:用
memcache和rediscache分别跑同一组用例,断言结果完全一致 - 嵌入结构体时尤其注意:若
type AdminUser struct{ User },AdminUser.Save()调用了和User.Save()不同的表或事务逻辑,就已破坏 LSP
真正难的不是写出符合 SOLID 的代码,而是每次加一行功能时,都停下来问一句:这个改动会让多少其他地方被迫修改?如果答案是“不止一处”,那职责已经粘连了——这时候删代码,比加代码更重要。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










