头图

译|What “accept interfaces, return structs” means in Go

“接受接口、返回结构” 的一般原则,我在前一篇文章中写到,也多次在代码评审时向同事介绍,但经常遇到“为什么”的疑问。特别是因为这不是一条硬性规定。该想法的关键在于保持灵活性的同时避免预先抽象,并理解何时改变它。

gopher.png

预先抽象使系统变得复杂

计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决,当然,间接过多的问题除外 - David J. Wheeler

软件工程师喜欢抽象。就我个人而言,从未见过编写代码比创建抽象更投入的同事。Go 中,接口抽象脱离了结构,该间接层甚至没有最低层次的嵌入复杂性。遵循软件设计 您不会用到它 的哲学,在需要之前制造这种复杂性毫无意义。函数调用返回接口的一个常见原因是让用户专注于函数开放的 API。因为有隐式接口,Go 不需要这样做。结构的 public function iu 就是其 API。

总是当 真正 需要时 [抽象],不要当 预见 需要时 [抽象]。

某些语言要求你预见未来需要的每个接口。隐式接口的一大优点是,它们允许事后进行优雅的抽象,而无需预先进行抽象。

因人而异的“需要”

当真正需要时

如何定义何时需要抽象?对于返回类型,这很容易。你是编写该函数的人,因此您确切知道何时需要将返回值抽象。

对于函数输入,需求不在你的控制范围之内。你可能认为 database struct 就足够了,但用户可能需要用其他东西装饰它。就算不是不可能,预测每个人使用你的函数的状态也是很困难的。能够精确控制输出,但无法预测用户输入。相比对输出的抽象化,这种不平衡造成了对输入的抽象化更强烈的偏重。

去除不必要的代码细节

recipes.png

简化的另一方面是去除不必要的细节。函数就像烹饪食谱:给定输入,就会得到一个蛋糕!没有食谱会列出不需要的配料。类似地,函数也不应该列出不需要的输入。你如何看以下函数?

func addNumbers(a int, b int, s string) int { 
    return a + b 
}

对于大多数程序员来说,很明显,参数 s 不恰当。当参数是结构时,却不太明显。

type Database struct{ } 
func (d *Database) AddUser(s string) {...} 
func (d *Database) RemoveUser(s string) {...}
func NewUser(d *Database, firstName string, lastName string) { 
    d.AddUser(firstName + lastName) 
}

就像配料太多的食谱一样,NewUser 接收一个可以做太多事情的 Database 对象。它只需要 AddUser,但接收的参数还有 RemoveUser。使用接口创建的函数,可以只依赖于必需。

type DatabaseWriter interface { 
    AddUser(string) 
} 
func NewUser(d DatabaseWriter, firstName string, lastName string) { 
    d.AddUser(firstName + lastName) 
}

Dave Cheney 在描述 接口隔离原则写到了这一点。他还描述了限制输入的其他优点,值得一读。让人理解这个想法的总目标是:

结果同时是一个函数,它的要求是最具体的——它只需要一个可写的东西——并且它的函数是最通用的

我只想补充一点,上面的函数 addNumber 显然不应该有参数字符串 s,函数 NewUser 理想情况下不需要可以删除用户的 database。

总结原因并审查例外

主要原因如下:

  • 去除不需要的抽象
  • 用户对函数输入需求是模糊的
  • 简化函数输入

以上原因还允许我们定义规则的例外情况。例如,如果函数需要返回多种类型,那么显然返回需要定义为接口。类似地,如果函数是私有的,那么函数输入便并不模糊,因为你可以控制它,所以倾向于非预先抽象。对于第三条规则,go 没有办法抽象出 struct 成员的值。因此,如果你的函数需要访问结构体成员(而不仅仅是结构体上的函数),那么您将被迫直接接受结构体。

原文:What “accept interfaces, return structs” means in Go

本文作者:cyningsun
本文地址https://www.cyningsun.com/08-...
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-ND 3.0 CN 许可协议。转载请注明出处!


12 声望
5 粉丝
0 条评论
推荐阅读
如何快速定位现网 BUG
“幸福的家庭都是相似的,不幸的家庭却各有各的不幸”,托尔斯泰的名言。在 BUG 定位这件事情上,其实也有类似的现象:”菜鸟们的紧张无措都是相似的,老鸟们的方法却各有各的不同”。

有疑说阅读 965

封面图
Golang 中 []byte 与 string 转换
string 类型和 []byte 类型是我们编程时最常使用到的数据结构。本文将探讨两者之间的转换方式,通过分析它们之间的内在联系来拨开迷雾。

机器铃砍菜刀24阅读 58.5k评论 2

万字详解,吃透 MongoDB!
MongoDB 是一个基于 分布式文件存储 的开源 NoSQL 数据库系统,由 C++ 编写的。MongoDB 提供了 面向文档 的存储方式,操作起来比较简单和容易,支持“无模式”的数据建模,可以存储比较复杂的数据类型,是一款非常...

JavaGuide8阅读 1.8k

封面图
数据结构与算法:二分查找
一、常见数据结构简单数据结构(必须理解和掌握)有序数据结构:栈、队列、链表。有序数据结构省空间(储存空间小)无序数据结构:集合、字典、散列表,无序数据结构省时间(读取时间快)复杂数据结构树、 堆图二...

白鲸鱼9阅读 5.4k

PHP转Go实践:xjson解析神器「开源工具集」
我和劲仔都是PHP转Go,身边越来越多做PHP的朋友也逐渐在用Go进行重构,重构过程中,会发现php的json解析操作(系列化与反序列化)是真的香,弱类型语言的各种隐式类型转换,很大程度的减低了程序的复杂度。

王中阳Go11阅读 2.8k评论 4

封面图
Git操作不规范,战友提刀来相见!
年终奖都没了,还要扣我绩效,门都没有,哈哈。这波骚Git操作我也是第一次用,担心闪了腰,所以不仅做了备份,也做了笔记,分享给大家。问题描述小A和我在同时开发一个功能模块,他在优化之前的代码逻辑,我在开...

王中阳Go6阅读 2.9k评论 4

封面图
妙啊,空结构体还能这么用?Go语言的结构体看这篇就够了
本文详解了Go语言结构体的各个知识点,最后介绍了空结构体的3种妙用。希望对你有帮助。定义结构体,是一种自定义的数据类型,由多个数据类型组合而成。用于描述一类事物相关属性。定义方式: {代码...} 实例化结...

王中阳Go6阅读 1.3k

封面图
12 声望
5 粉丝
宣传栏