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}

}

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

相关推荐
未若君雅裁13 分钟前
微服务监控与 SkyWalking 链路追踪
微服务·架构·skywalking
高级c23 分钟前
hccl 集合通信架构剖析:Ring-AllReduce 与通信-计算重叠设计
架构
心中有国也有家25 分钟前
hccl 架构拆解:昇腾集合通信库到底在做什么?
人工智能·经验分享·笔记·分布式·算法·架构
heimeiyingwang27 分钟前
【架构实战】可观测性体系:从监控到全链路追踪
网络·数据库·架构
菩提树下的凡夫29 分钟前
FACE 与 AUTOSAR 开放架构标准的比较分析
架构
ㄣ知冷煖★1 小时前
统一网关架构实践:从 Token 鉴权到路由、策略与凭证池转发全链路解析
java·服务器·架构
GISer_Jing1 小时前
Three.JS渲染架构解读
java·javascript·架构
IT_陈寒2 小时前
Vue的computed属性怎么突然不更新了?
前端·人工智能·后端
2401_868534782 小时前
论大数据架构的应用
架构
invicinble2 小时前
spring提供的其他机制
java·后端·spring