Go语言中常见100问题-#7 尽量不要返回接口

尽量不要返回接口

在设计函数签名时,函数的返回值要么是一个接口,要么是一个具体类型。本文将分析为什么在很多情况下返回接口在Go语言中不是一种好的做法。在Go语言中常见100问题-#6 生产者端接口中讨论了接口通常定义在消费者端。通过下图,来看看如果一个函数返回的是一个接口而不是具体类型,依赖关系会发生什么变化,这会导致什么问题。下图中包含store和client两个包,client包中定义了一个Store接口,store包中 InMemoryStore 结构体实现了Store接口。

在store包中定义了一个实现Store接口的InMemoryStore结构体,同时创建一个 NewInMemoryStore 函数,该函数的返回值为一个Store接口。这样设计编码会导致store包和client包之间存在依赖关系。例如,客户端client不能再调用 NewInMemoryStore 函数,否则将导致循环依赖。解决这种循环依赖的一种可能方法是从另外一个包中调用此函数并将Store实现注入到客户端中。然而,被迫这样做意味编码设计应该受到讨论和质疑。同样,如果有另一个客户端需要使用 InMemoryStore 结构体,怎么办呢?将Store接口移动到另一个包中?还是将其定义到store包中?处理起来都不优雅,像是一种代码坏味道。说了这么多,是想表达为什么在大多数情况下它不是最佳实践。

因此,通常来说,返回一个接口会限制灵活性,因为这会强制所有客户端使用一种特定类型的抽象。在大多数情况下,我们可以遵从伯斯塔尔法则(Postel law).

对内保存保守,对外保持自由

如果我们把这个法则应用到Go语言编程中,想表达的意思是:

  • 返回结构体而不是接口

  • 尽可能接收接口

当然,也有一些例外情况。作为软件工程师,我们熟知没有一成不变的规则,即在100%的情况下,规则永远不会是真的。最好的例子证明是错误类型,很多函数都会返回一个接口类型的错误(error). 我们还可以使用io包检查标准库中的另外一个异常,像下面的函数返回一个可导出的结构体:io.LimitedReader,但是函数的签名是一个接口:io.Reader, 这不是不符合我们前面的讨论分析吗,为什么要这样实现呢?因为io.Reader是一个提前确定的抽象,它不是由客户端定义的,而是强制存在的,这其实就是前一篇文章中讲的生产者端接口,语言设计者事先知道这种抽象级别在可重用性和可组合性方面有帮助。

golang 复制代码
func LimitReader(r Reader, n int64) Reader {

return &LimitedReader{r, n}

}

总而言之,在大多数情况下,我们不应该返回接口,而是返回具体的实现。否则,由于存在包依赖性和灵活性受到限制,会使我们的设计编码更加复杂。因为所有的客户端都必须依赖相同的抽象。如果我们知道(不是预想)抽象对客户端有所帮助,可以考虑返回一个接口。否则,我们不应该强制抽象,应该交给客户端发现。如果客户端处于某种原因需要抽象实现,它可以将抽象定义在自己包中,这样具有很强的灵活性。

相关推荐
蔡俊锋24 分钟前
DeepSeek 语音对话灰度上线:四种音色背后的端侧 AI 交互架构与商业逻辑
架构·大模型·语音交互·deepseek·端侧ai
IT_陈寒1 小时前
Java Stream处理大集合,我的内存怎么就炸了
前端·人工智能·后端
悟空码字1 小时前
四轮对话两张配图:用 WorkBuddy 优化公众号发文配图的实战指南
人工智能·后端·腾讯
ServBay1 小时前
xAI的 Grok Bot发布,AI 已经学会自己上班了
后端·ai编程·grok
分支预测失败1 小时前
RISC-V 多核缓存一致性机制解析:MESI 协议族、CMO 扩展与 Linux 同步原语
后端
云边有个稻草人1 小时前
OceanBaseVS金仓:从架构效率到复杂SQL,解析金仓数据库的性能竞争力
架构·数据库架构·数据库性能·数据库选型·oceanbasevs金仓·复杂sql优化·事务性能
n8n1 小时前
Spring AI 检索增强生成(RAG)实战:让模型掌握你的私有知识
后端
Nturmoils2 小时前
只备份一个 schema,别把整库都搬走
后端
妙码生花2 小时前
利用AI从零学Go并完成实战项目,完工总结:目录结构
前端·后端·go
妙码生花2 小时前
利用AI从零学Go并完成实战项目,完工总结:商业级开源产品定位和核心特性介绍
前端·后端·go