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}

}

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

相关推荐
星释3 小时前
Rust 练习册 :Leap与日期计算
开发语言·后端·rust
七宝大爷4 小时前
多GPU并行计算互联架构解析:NVLink的诞生与SLI CrossFire的落幕
架构·nvlink·sli
忙碌5445 小时前
AI大模型时代下的全栈技术架构:从深度学习到云原生部署实战
人工智能·深度学习·架构
码事漫谈7 小时前
C++死锁深度解析:从成因到预防与避免
后端
码事漫谈7 小时前
智能体颠覆教育行业:现状、应用与未来展望调研报告
后端
蓝-萧7 小时前
【玩转全栈】----Django基本配置和介绍
java·后端
priority_key7 小时前
排序算法:堆排序、快速排序、归并排序
java·后端·算法·排序算法·归并排序·堆排序·快速排序
韩立学长7 小时前
基于Springboot的旧时月历史论坛4099k6s9(程序、源码、数据库、调试部署方案及开发环境)系统界面展示及获取方式置于文档末尾,可供参考。
数据库·spring boot·后端
汤姆yu8 小时前
基于SpringBoot的动漫周边商场系统的设计与开发
java·spring boot·后端
灰小猿8 小时前
Spring前后端分离项目时间格式转换问题全局配置解决
java·前端·后端·spring·spring cloud