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}

}

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

相关推荐
ACP广源盛139246256731 分钟前
GSV6155@ACP# Type-C 视频信号转换芯片:技术架构、工程痛点适配与落地场景分析
大数据·c语言·开发语言·人工智能·分布式·嵌入式硬件·架构
铁皮饭盒6 分钟前
网页端OCR, 加载6mb大小模型, 又快又准, 百度这次真香
前端·后端
程序员-Benothing9 分钟前
Java集合:List实现类深度对比
java·开发语言·后端·面试·list
云浪32 分钟前
给 AI Agent 加上语音交互:ASR + 流式 TTS 实战
前端·人工智能·后端
iLinuxUser36 分钟前
V4L2 架构文档
架构
卷无止境38 分钟前
当代码要上线前,谁在替你把关?——Python安全审查的方法论与实践
后端·python
卷无止境1 小时前
Python Dataclasses:让类定义回归简洁的艺术
后端·python
沉迷学习 日益消瘦1 小时前
20-综合实战:微服务部署
微服务·云原生·架构·kubernetes
前端工作日常10 小时前
我学习到的Java 的 Service 分层:itf 和 impl 到底是什么?
java·后端