Golang 函数 vs 方法 (译文) | Go主题月

1,142 阅读3分钟

2019年6月4日

这篇文章将描述 Go 中函数和方法之间的主要区别,以及如何最好地使用它们。

image.png

函数和方法都在 Go 中广泛使用,提供了抽象,使我们的程序更易于阅读和推理。从表面上看,函数和方法看起来都很相似,但是存在一些重要的语义差异,这些差异会对代码的可读性产生很大影响。

语法

声明语法

通过指定参数类型、返回值和函数体来声明函数:

type Person struct {
  Name string
  Age int
}

// 此函数返回 Person 的新实例
func NewPerson(name string, age int) *Person {
  return &Person{
    Name: name,
    Age: age,
  }
}

另一方面,通过另外指定 「接收者」 来声明方法(在 OOP 术语中,「接收者」 是该方法所属的 「类 class」 ):

// Person 指针类型是 isAdult 方法的接收器
func (p *Person) isAdult() bool {
  return p.Age > 18
}

在上面的方法声明中,我们对 *Person 类型声明了 isAdult 方法。

执行语法

函数是用指定的参数独立调用的,方法是根据其接收者的类型调用的:

p := NewPerson("John", 21)

fmt.Println(p.isAdult())
// true

互换性

函数和方法理论上可以互换。例如,我们可以将 isAdult 方法转换为函数,并将 NewPerson 函数作为方法:

type PersonFactory struct {}

// `NewPerson` is now a method of a `PersonFactory` struct
func (p *PersonFactory) NewPerson(name string, age int) *Person {
  return &Person{
    Name: name,
    Age: age,
  }
}

// `isAdult` is now a function where we're passing the `Person` as an argument
// instead of a receiver
func isAdult(p *Person) bool {
  return p.Age > 18
}

本例中的执行语法看起来有点奇怪:

factory := &PersonFactory{}

p := factory.NewPerson("John", 21)

fmt.Println(isAdult(p))
// true

上面的代码看起来比实际需要的更不必要和复杂。这表明,方法和函数的区别主要是语法上的,您应该根据用例使用适当的抽象。

用例

让我们来看看 Go 应用程序中遇到的一些常见用例,以及为每个应用程序使用的适当抽象(函数或方法):

方法链接

方法的一个非常有用的特性是能够将它们链接在一起,同时保持代码的干净。让我们以使用链接设置 Person 的某些属性为例:

type Person struct {
	Name string
	Age  int
}

func (p *Person) withName(name string) *Person {
	p.Name = name
	return p
}

func (p *Person) withAge(age int) *Person {
	p.Age = age
	return p
}

func main() {
	p := &Person{}

	p = p.withName("John").withAge(21)
	
  fmt.Println(*p)
  // {John 21}
}

如果我们将函数用于同一事物,它看起来会非常可怕:

p = withName(withAge(p, 18), "John")

有状态与无状态执行

在互换性示例中,我们看到了使用 PersonFactory 对象来创建 person 的新实例。事实证明,这是一种反模式,应该避免。

对于无状态执行,最好使用像前面声明的 NewPerson 函数这样的函数。

这里的「无状态」指的是对于相同的输入总是返回相同输出的任何代码段

这里的推论是,如果您发现一个函数读取和修改了一个特定类型的很多值,那么它应该是该类型的一个方法。

语义

语义是指代码的读取方式。如果你用你的口语大声朗读代码,什么更有意义?

让我们看看 isAdult 的函数和方法实现

customer := NewPerson("John", 21)

// Method
customer.isAdult()

// Function
isAdult(customer)

在这里,对于询问 "Is the customer an adult?" , customer.isAdult()isAdult(customer) 好,当你问 "is x and adult?" ?它总是在 Person x 的上下文中被问到。

结论

尽管我们讨论了 Go 中函数和方法的一些关键区别和用例,但总有例外!重要的是不要把这些规则当作一成不变的。

最后,函数和方法之间的区别在于结果代码的读取方式。如果您或您的团队认为一种方法比另一种方法读起来更好,那么这就是正确的抽象!

原文链接:www.sohamkamani.com/golang/func…