译者:baiyutang
原文:https://www.digitalocean.com/community/tutorials/how-to-use-interfaces-in-go
介绍
编写灵活、可复用和模块化的代码对开发通用性程序是至关重要的。以这种方式工作的代码易于维护、避免需要在多处修改相同的代码。做到这一点,因语言而异。对于接口,继承是一个被使用的共同的方式,像 Java、C++、C#等等。
通过组合,开发者也能实现相同的目标。组合是联合对象或类型到更复杂之中的方式。这是 Go 用来提高代码可复用、模块化和灵活性的方式。Go 的接口提供了组织复杂组成的方式,学习如何使用接口将允许你创建通用性、可复用的代码。
这篇文章中,我们将学习如何组合具有常见行为的自定义类型,将允许我们复用代码。我们也能学习如何实现我们自定义类型的接口,以满足从另外一个包中定义接口。
定义行为
组合的核心实现之一就是接口的使用。一个接口定义了一种类型的一个行为。Go 标准库中最普遍使用的接口是 fmt.Stringer 接口:
 type Stringer interface {    String() string}
       复制代码
 
第一行代码定义了名为 Stringer 的类型。然后它声明是一个接口。就想定义结构体,Go 使用花括号({})包裹接口的定义。相较于定义结构体,我们只需要定义接口的行为,就是“这个类型能做什么”。
在 Stringer 的接口例子中,唯一的行为是 String() 方法,该方法不接受参数并返回一个字符。
然后,让我们看看具有 fmt.Stringer 行为的代码:
 package main
import "fmt"
type Article struct {    Title string    Author string}
func (a Article) String() string {    return fmt.Sprintf("The %q article was written by %s.", a.Title, a.Author)}
func main() {    a := Article{        Title: "Understanding Interfaces in Go",        Author: "Sammy Shark",    }    fmt.Println(a.String())}
       复制代码
 
我们要做的第一件事是创建一个名为 Article 的新类型,这个类型具有 Title 和 Author 字段并且他们都是 字符类型。
 ...type Article struct {    Title string    Author string}...
       复制代码
 
然后,在我们的 main 函数,我们创建一个 Article 的接口类型并赋值给一个名为 a 的变量。我们给 Title 字段提供值为 "Understanding Interfaces in Go",给 Author 提供值为 "Sammy Shark":
 ...a := Article{    Title: "Understanding Interfaces in Go",    Author: "Sammy Shark",}...
       复制代码
 
然后,我们通过调用 fmt.Println 打印 String 方法的结果。并且把 a.String 方法调用的结果传过去。
 ...fmt.Println(a.String())
       复制代码
 
在运行程序之后,我们将看到下面的输出:
 The "Understanding Interfaces in Go" article was written by Sammy Shark.
       复制代码
 
到目前为止,我们没有使用接口,但是我们刚创建具有一个行为的类型。行为匹配到 fmt.Stringer 接口。然后,让我们看看我们如何使用该行为让我们的代码更可复用。
定义接口
 package main
import "fmt"
type Article struct {    Title string    Author string}
func (a Article) String() string {    return fmt.Sprintf("The %q article was written by %s.", a.Title, a.Author)}
func main() {    a := Article{        Title: "Understanding Interfaces in Go",        Author: "Sammy Shark",    }    Print(a)}
func Print(a Article) {    fmt.Println(a.String())}
       复制代码
 
接口中的多个行为
 package main
import (    "fmt"    "math")
type Circle struct {    Radius float64}
func (c Circle) Area() float64 {    return math.Pi * math.Pow(c.Radius, 2)}
type Square struct {    Width  float64    Height float64}
func (s Square) Area() float64 {    return s.Width * s.Height}
type Sizer interface {    Area() float64}
func main() {    c := Circle{Radius: 10}    s := Square{Height: 10, Width: 5}
    l := Less(c, s)    fmt.Printf("%+v is the smallest\n", l)}
func Less(s1, s2 Sizer) Sizer {    if s1.Area() < s2.Area() {        return s1    }    return s2}
       复制代码
 
 package main
import (    "fmt"    "math")
type Circle struct {    Radius float64}
func (c Circle) Area() float64 {    return math.Pi * math.Pow(c.Radius, 2)}
func (c Circle) String() string {    return fmt.Sprintf("Circle {Radius: %.2f}", c.Radius)}
type Square struct {    Width  float64    Height float64}
func (s Square) Area() float64 {    return s.Width * s.Height}
func (s Square) String() string {    return fmt.Sprintf("Square {Width: %.2f, Height: %.2f}", s.Width, s.Height)}
type Sizer interface {    Area() float64}
type Shaper interface {    Sizer    fmt.Stringer}
func main() {    c := Circle{Radius: 10}    PrintArea(c)
    s := Square{Height: 10, Width: 5}    PrintArea(s)
    l := Less(c, s)    fmt.Printf("%v is the smallest\n", l)
}
func Less(s1, s2 Sizer) Sizer {    if s1.Area() < s2.Area() {        return s1    }    return s2}
func PrintArea(s Shaper) {    fmt.Printf("area of %s is %.2f\n", s.String(), s.Area())}
       复制代码
 总结
我们已经看到如何创建更小的接口,并将其构建为更大的接口,从而只共享我们需要的功能或方法。我们也学到了我们可以从其他接口组合我们接口,包括这些从其他包定义的接口,不仅仅是我们的。
评论