Bootstrap

Go 中的 7 个常见接口错误

Go 仍然是一门新语言,如果你正在使用它,它很可能不是你的第一门编程语言。

不同的语言,既为你带来了经验,也带来了偏见。你用以前的任何语言做的事情,在 Go 中用相同的方法可能不是一个好主意。

学习 Go 不仅仅是学习一种新的语法。这也是学习一种新的思维方式来思考你的程序:

Go is a language for writing Go programs, not Java programs or Haskell programs or any other language’s programs. You need to think a different way to write good Go programs. But that takes time and effort, more than most will invest. So the usual story is to translate one program from another language into Go and see how it turns out. But translation misses idiom. A first attempt to write, for example, some Java construct in Go will likely fail, while a different Go-specific approach might succeed and illuminate. After 10 years of Java programming and 10 minutes of Go programming, any comparison of the language’s capabilities is unlikely to generate insight, yet here come the results, because that’s a modern programmer’s job.

Rob Pike Esmerelda’s Imagination

正如 Rob Pike 所建议的那样,如果你想提高围棋技能,需要投入时间和精力来学习这门语言的习语。

Go 在几件事上与其他传统语言不同,在本文中,我将重点介绍其中之一:接口。

下面列出了人们在编写 Go 接口时常犯的错误。这些在其他语言中可能不是错误,但在 Go 中,你需要忘记它们。或者至少,给一个机会,暂时不和他们一起工作,看看这会把你引向何方。

但在这之前

以下是阅读本文时要记住的事项列表。如果你已经熟悉它们,请随意跳过。

  • 接口隔离原则:不应强制客户端实现它不使用的接口,也不应强制客户端依赖于它们不使用的方法。

  • 多态性:一段代码根据它收到的具体数据改变其行为。

  • Liskov 替换原则:如果你的代码依赖于抽象,那么一个实现可以被另一个实现替换,而无需更改你的代码。

    抽象的目的不是模糊不清,而是创造一个新的语义层次,在这个层次上可以绝对精确。— E.W.迪克斯特拉

接口是精确包含用于编写程序的想法的概念。

以正确的方式使用接口可以带来简单性、可读性和 organic code 设计。

organic code 是代码会根据你在某个时间点所需的行为而增长。它不会强迫你提前考虑你的类型以及它们之间的关系,因为你很可能无法正确理解它们。

这就是为什么说 Go 偏爱组合而不是继承。你有一小组行为,你可以从中编写任何你想要的,而不是预定义由其他类型继承的类型并希望它们适合问题域。

Rob Pike 在 golang-nuts 论坛上解释了这种方法:

img

Go 的接口不是 Java 或 C# 接口的变体,它们远不止于此。它们是大规模编程和适应性强的演进设计的关键。

无论如何,理论已经足够了,让我们来看看最常见的错误:

1. 你创建了太多的接口

接口过多的术语称为接口污染 interface pollution.。当你在编写具体类型之前开始抽象时,就会发生这种情况。由于你无法预见你需要什么抽象,所以很容易写出太多的接口,这些接口在以后要么是错误的,要么是无用的。

Rob Pike 有一个很好的指南,可以帮助我们避免界面污染:

Don’t design with interfaces, discover them.

Rob Pike

Rob 在这里指出的是,你不需要提前考虑你需要什么抽象。你可以使用具体结构开始设计,并仅在设计需要时创建接口。通过这样做,你的代码会顺其自然的增长到预期的设计。

我仍然看到人们提前创建接口,因为他们认为他们将来可能需要多个实现。

我对他们说:

img

以一种好的方式懒惰。创建接口的最佳时机是你真正需要它的时候,而不是你预测需要它的时候。下面是一个通过提前思考创建接口的示例,以及它导致了什么。

无用的接口往往只有一个实现。它们只是增加了一个额外的间接级别,迫使程序员在真正想要实现时总是通过它们。

接口是有代价的:这是您在推理代码时需要记住的一个新概念。正如 Djikstra 所说,理想的界面必须是“a new semantic level in which one can be absolutely precise.”。

如果你的代码需要 Box 的概念,仅由 Box 实现的名为 Container 的额外接口没有带来任何好处,除了混淆。

因此,在创建接口之前,先问问自己:接口有多个实现吗?我强调使用了‘有’,因为‘将会有’假设了你能预测未来,而你不能。

2. 你有太多的方法

在 PHP 项目中,看到 10 种方法接口是很常见的。在 Go 中,接口很小,标准库中所有接口上的平均方法数为 2。

The bigger the interface the weaker the abstraction,这是 Go 谚语之一。正如 Rob Pike 所说,这是接口最重要的一点,这意味着接口越小,它就越有用。

接口可以拥有的实现越多,它的通用性就越强。如果你有一个包含大量方法的接口,则很难有它的多个实现。您拥有的方法越多,接口就越具体。它越具体,不同类型显示相同行为的可能性就越低。

有用接口的一个很好的例子是

;