Go项目接口组织疑问:接口是否应定义在使用方包中?
Go接口定义位置的困惑解答
首先明确:Go Wiki的表述是接口设计的核心原则,但并非绝对的硬性规则,实际开发中要结合场景灵活调整。
为什么推荐在使用方定义接口?
- 接口的本质是使用方对依赖的抽象:使用方只需要关心自己用到的方法,不需要了解实现方的全部能力。比如你的20个包如果只需要
GetCache,那它们定义的接口就只包含这个方法,Tester的GetDB方法变更完全不会影响到这些使用方。 - 实现解耦:使用方只依赖自己定义的接口,而非直接依赖
test包的Tester类型。如果以后出现另一个实现GetCache的结构体(比如RedisCache),使用方不需要修改任何代码,只要传入符合接口的实例就能正常工作。
你的场景如何解决维护性问题?
如果20个包都需要同一个GetCache接口,完全没必要每个包重复定义,有两种实用方案:
- 抽离公共接口包:新建一个比如
cache的公共包,在里面统一定义接口:
所有需要用到这个接口的包都导入package cache type CacheGetter interface { GetCache(key string) (value string, error) }cache包,直接使用cache.CacheGetter作为参数类型。后续接口契约变更时,只需要修改cache包的定义,所有依赖包同步更新即可,避免重复劳动。 - 实现方提供通用接口(可选):如果
test包本身希望对外暴露一套标准能力,也可以在test包定义接口,但要注意:这个接口应该是实现方希望对外提供的通用能力集合,而非为了满足某个使用方的特殊需求。比如:
此时使用方可以选择直接用这个公共接口,也可以根据自身需求定义更精简的接口。package test type CacheInterface interface { GetCache(key string) (value string, error) }
什么时候适合在实现方定义接口?
- 当接口是通用能力标准时:比如标准库的
io.Reader、io.Writer,它们定义在io包中,因为绝大多数涉及IO操作的包都需要这些通用能力,统一定义能降低使用成本。 - 当实现方是框架/插件体系时:比如框架提供的插件接口,通常会在框架包中定义,强制插件实现方遵循规范,方便框架统一调用。
总结
- 优先遵循“使用方定义接口”的原则,这是Go推崇的依赖倒置实践,能有效降低代码耦合度。
- 多使用方共用同一接口时,抽离公共接口包是最优解,既符合设计原则又保证维护性。
- 实现方定义接口是补充场景,适用于通用能力标准或框架规范类需求。
内容的提问来源于stack exchange,提问作者Andfernan
相关产品推荐
相关产品推荐

