【Go Interface】接口诞生的意义
结论:接口(Interface)诞生的唯一意义:解耦
接口的诞生,是为了解决软件工程里最致命的痛点:“上层代码”被“底层细节”死死绑架。
没有接口时的痛苦
假设你的naga模块现在要保存心跳数据。
第一周,你用的是 Redis 缓存,你在repo里写了一个结构体:
type RedisRepo struct{} func (r *RedisRepo) SaveHeartbeat(data string) { /* 写入 Redis */ }这时候,你的上层业务逻辑(logic层)为了调用它,就必须在结构体里死死绑定制冷工具:
type ServerLogic struct { Dao *RedisRepo // 💥 致命痛点:这里写死了 Redis! } func (l *ServerLogic) Execute(data string) { l.Dao.SaveHeartbeat(data) }灾难来了:第二周,老板说 Redis 内存太贵了,心跳必须改存到 MySQL 数据库里!
你高高兴兴写完了MySQLRepo,回头一看ServerLogic,你当场就崩溃了——因为里面写死了*RedisRepo!你必须把整个logic层全部翻出来,把类型改成*MySQLRepo。如果整个大系统有 50 个地方写死了 Redis,你就得改 50 个地方。
有了接口(Interface)后的降维打击
接口的本质,就是上层 logic 层对底层 repo 层下达的一份“悬赏通缉令(契约)”。
Logic 层现在不当传话筒了,它直接在自己的包里宣布:
“我不管你底层用 Redis、MySQL 还是机械硬盘,只要谁能做到SaveHeartbeat(string)这个行为,谁就可以来当我(ServerLogic)的打工仔!”
// 1. 定义一个接口(只定义行为,不定义数据) type HeartbeatSaver interface { SaveHeartbeat(data string) } // 2. Logic 层现在只绑定这个抽象的“悬赏令”,不绑定任何具体数据库! type ServerLogic struct { Dao HeartbeatSaver // 🚀 奇迹发生了:这里是一个接口类型! }现在,你的RedisRepo和MySQLRepo只需要老老实实实现SaveHeartbeat这个方法。
- 想用 Redis?直接把
RedisRepo传给 Logic。 - 想换 MySQL?Logic 层的代码连一个标点符号都不用改,直接把
MySQLRepo传过去!
总结:上层稳定的代码,去适配底层多变的代码
Interface 出现的意义,就是让上层稳定(不容易产生 Bug)的代码,去适配底层多变(天天改需求)的代码。 在大厂里,只要涉及到“需要切换实现”(如:换数据库、换日志库、Mock 测试),就必然会出现 Interface。
