教程集 www.jiaochengji.com
教程集 >  Golang编程  >  golang教程  >  正文 golang接口的使用场景_如何在Go中使用接口

golang接口的使用场景_如何在Go中使用接口

发布时间:2022-02-16   编辑:jiaochengji.com
教程集为您提供golang接口的使用场景,如何在Go中使用接口等资源,欢迎您收藏本站,我们将为您提供最新的golang接口的使用场景,如何在Go中使用接口资源

golang接口的使用场景

<h3 id="introduction"> 介绍 <span style="font-weight: bold;">(</span>Introduction<span style="font-weight: bold;">)</span></h3>

Writing flexible, reusable, and modular code is vital for developing versatile programs. Working in this way ensures code is easier to maintain by avoiding the need to make the same change in multiple places. How you accomplish this varies from language to language. For instance, <em>inheritance</em> is a common approach that is used in languages such as Java, C , C#, and more.

编写灵活,可重用和模块化的代码对于开发通用程序至关重要。 通过这种方式工作,避免了在多个位置进行相同更改的需要,从而确保了代码的维护更加容易。 您完成此操作的方式因语言而异。 例如, <em>继承</em>是Java,C ,C#等语言使用的一种常见方法。

Developers can also attain those same design goals through <em>composition</em>. Composition is a way to combine objects or data types into more complex ones. This is the approach that Go uses to promote code reuse, modularity, and flexibility. Interfaces in Go provide a method of organizing complex compositions, and learning how to use them will allow you to create common, reusable code.

开发人员还可以通过<em>合成</em>来达到相同的设计目标。 组合是一种将对象或数据类型组合为更复杂的方法。 这是Go用来促进代码重用,模块化和灵活性的方法。 Go中的接口提供了一种组织复杂的合成的方法,学习如何使用它们将使您可以创建通用的可重用代码。

In this article, we will learn how to compose custom types that have common behaviors, which will allow us to reuse our code. We’ll also learn how to implement interfaces for our own custom types that will satisfy interfaces defined from another package.

在本文中,我们将学习如何编写具有共同行为的自定义类型,这将使​​我们能够重用我们的代码。 我们还将学习如何为自己的自定义类型实现接口,这些接口将满足从另一个包定义的接口。

<h2 id="defining-a-behavior"> 定义行为 <span style="font-weight: bold;">(</span>Defining a Behavior<span style="font-weight: bold;">)</span></h2>

One of the core implementations of composition is the use of interfaces. An interface defines a behavior of a type. One of the most commonly used interfaces in the Go standard library is the <code>fmt.Stringer</code> interface:

组合的核心实现之一是接口的使用。 接口定义类型的行为。 Go标准库中最常用的接口之一是<code>fmt.Stringer</code>接口:

<pre class="has"><code class="code-highlight language-go">type Stringer interface { String() string }</code></pre>

The first line of code defines a <code>type</code> called <code>Stringer</code>. It then states that it is an <code>interface</code>. Just like defining a struct, Go uses curly braces (<code>{}</code>) to surround the definition of the interface. In comparison to defining structs, we only define the interface’s <em>behavior</em>; that is, “what can this type do”.

代码的第一行定义了一个称为<code>Stringer</code>的<code>type</code> 。 然后声明它是一个<code>interface</code> 。 就像定义一个结构一样,Go使用花括号( <code>{}</code> )来包围接口的定义。 与定义结构相比,我们仅定义接口的<em>行为</em> ; 也就是说,“这种类型可以做什么”。

In the case of the <code>Stringer</code> interface, the only behavior is the <code>String()</code> method. The method takes no arguments and returns a string.

对于<code>Stringer</code>接口,唯一的行为是<code>String()</code>方法。 该方法不带参数,并返回一个字符串。

Next, let’s look at some code that has the <code>fmt.Stringer</code> behavior:

接下来,让我们看一些具有<code>fmt.Stringer</code>行为的代码:

main.go
main.go
<pre class="has"><code class="code-highlight language-go">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()) }</code></pre>

The first thing we do is create a new type called <code>Article</code>. This type has a <code>Title</code> and an <code>Author</code> field and both are of the string data type:

我们要做的第一件事是创建一个称为<code>Article</code>的新类型。 此类型具有<code>Title</code>和<code>Author</code>字段,并且均为字符串数据类型 :

main.go
main.go
<pre class="has"><code class="code-highlight language-go">... type Article struct { Title string Author string } ...</code></pre>

Next, we define a <code>method</code> called <code>String</code> on the <code>Article</code> type. The <code>String</code> method will return a string that represents the <code>Article</code> type:

接下来,我们在<code>Article</code>类型上定义一个称为<code>String</code>的<code>method</code> 。 <code>String</code>方法将返回一个表示<code>Article</code>类型的字符串:

main.go
main.go
<pre class="has"><code class="code-highlight language-go">... func (a Article) String() string { return fmt.Sprintf("The %q article was written by %s.", a.Title, a.Author) } ...</code></pre>

Then, in our <code>main</code> function, we create an instance of the <code>Article</code> type and assign it to the variable called <code>a</code>. We provide the values of <code>"Understanding Interfaces in Go"</code> for the <code>Title</code> field, and <code>"Sammy Shark"</code> for the <code>Author</code> field:

然后,在我们的<code>main</code> 函数中 ,我们创建<code>Article</code>类型的实例,并将其分配给名为<code>a</code>的变量 。 我们为<code>"Understanding Interfaces in Go"</code> <code>Title</code>字段提供<code>"Understanding Interfaces in Go"</code>的值,为“ <code>Author</code>字段提供<code>"Sammy Shark"</code>值:

main.go
main.go
<pre class="has"><code class="code-highlight language-go">... a := Article{ Title: "Understanding Interfaces in Go", Author: "Sammy Shark", } ...</code></pre>

Then, we print out the result of the <code>String</code> method by calling <code>fmt.Println</code> and passing in the result of the <code>a.String()</code> method call:

然后,我们通过调用<code>fmt.Println</code>并传入<code>a.String()</code>方法调用的结果来打印出<code>String</code>方法的结果:

main.go
main.go
<pre class="has"><code class="code-highlight language-go">... fmt.Println(a.String())</code></pre>

After running the program you’ll see the following output:

运行该程序后,您将看到以下输出:

<pre class="has"><code>
Output
The "Understanding Interfaces in Go" article was written by Sammy Shark. </code></pre>

So far, we haven’t used an interface, but we did create a type that had a behavior. That behavior matched the <code>fmt.Stringer</code> interface. Next, let’s see how we can use that behavior to make our code more reusable.

到目前为止,我们还没有使用接口,但是我们确实创建了具有行为的类型。 该行为与<code>fmt.Stringer</code>接口匹配。 接下来,让我们看看如何使用该行为使代码更可重用。

<h2 id="defining-an-interface"> 定义接口 <span style="font-weight: bold;">(</span>Defining an Interface<span style="font-weight: bold;">)</span></h2>

Now that we have our type defined with the desired behavior, we can look at how to use that behavior.

现在,我们已经定义了具有所需行为的类型,我们可以看看如何使用该行为。

Before we do that, however, let’s look at what we would need to do if we wanted to call the <code>String</code> method from the <code>Article</code> type in a function:

但是,在执行此操作之前,让我们先看看如果要从函数的<code>Article</code>类型调用<code>String</code>方法,我们需要做些什么:

main.go
main.go
<pre class="has"><code>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()) }</code></pre>

In this code we add a new function called <code>Print</code> that takes an <code>Article</code> as an argument. Notice that the only thing the <code>Print</code> function does is call the <code>String</code> method. Because of this, we could instead define an interface to pass to the function:

在此代码中,我们添加了一个名为<code>Print</code>的新函数,该函数将<code>Article</code>作为参数。 请注意, <code>Print</code>函数唯一要做的就是调用<code>String</code>方法。 因此,我们可以改为定义一个接口以传递给该函数:

main.go
main.go
<pre class="has"><code>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) } type Stringer interface { String() string } func main() { a := Article{ Title: "Understanding Interfaces in Go", Author: "Sammy Shark", } Print(a) } func Print(s Stringer) { fmt.Println(s.String()) }</code></pre>

Here we created an interface called <code>Stringer</code>:

在这里,我们创建了一个名为<code>Stringer</code>的接口:

main.go
main.go
<pre class="has"><code class="code-highlight language-go">... type Stringer interface { String() string } ...</code></pre>

The <code>Stringer</code> interface has only one method, called <code>String()</code> that returns a <code>string</code>. A method is a special function that is scoped to a specific type in Go. Unlike a function, a method can only be called from the instance of the type it was defined on.

<code>Stringer</code>接口只有一种方法,称为<code>String()</code> ,该方法返回<code>string</code> 。 方法是一种特殊的功能,范围仅限于Go中的特定类型。 与函数不同,只能从定义类型的实例中调用方法。

We then update the signature of the <code>Print</code> method to take a <code>Stringer</code>, and not a concrete type of <code>Article</code>. Because the compiler knows that a <code>Stringer</code> interface defines the <code>String</code> method, it will only accept types that also have the <code>String</code> method.

然后,我们更新<code>Print</code>方法的签名以采用<code>Stringer</code>而不是<code>Article</code>的具体类型。 因为编译器知道<code>Stringer</code>接口定义了<code>String</code>方法,所以它将仅接受也具有<code>String</code>方法的类型。

Now we can use the <code>Print</code> method with anything that satisfies the <code>Stringer</code> interface. Let’s create another type to demonstrate this:

现在,我们可以将<code>Print</code>方法与满足<code>Stringer</code>接口的任何方法一起使用。 让我们创建另一种类型来演示这一点:

main.go
main.go
<pre class="has"><code>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) } type Book struct { Title string Author string Pages int } func (b Book) String() string { return fmt.Sprintf("The %q book was written by %s.", b.Title, b.Author) } type Stringer interface { String() string } func main() { a := Article{ Title: "Understanding Interfaces in Go", Author: "Sammy Shark", } Print(a) b := Book{ Title: "All About Go", Author: "Jenny Dolphin", Pages: 25, } Print(b) } func Print(s Stringer) { fmt.Println(s.String()) }</code></pre>

We now add a second type called <code>Book</code>. It also has the <code>String</code> method defined. This means it also satisfies the <code>Stringer</code> interface. Because of this, we can also send it to our <code>Print</code> function:

现在,我们添加第二种类型,称为<code>Book</code> 。 它还定义了<code>String</code>方法。 这意味着它也满足<code>Stringer</code>接口。 因此,我们还可以将其发送到我们的<code>Print</code>函数:

<pre class="has"><code>
Output
The "Understanding Interfaces in Go" article was written by Sammy Shark. The "All About Go" book was written by Jenny Dolphin. It has 25 pages. </code></pre>

So far, we have demonstrated how to use just a single interface. However, an interface can have more than one behavior defined. Next, we’ll see how we can make our interfaces more versatile by declaring more methods.

到目前为止,我们已经演示了如何仅使用单个接口。 但是,一个接口可以定义多个行为。 接下来,我们将看到如何通过声明更多方法来使我们的界面更加通用。

<h2 id="multiple-behaviors-in-an-interface"> 接口中的多种行为 <span style="font-weight: bold;">(</span>Multiple Behaviors in an Interface<span style="font-weight: bold;">)</span></h2>

One of the core tenants of writing Go code is to write small, concise types and compose them up to larger, more complex types. The same is true when composing interfaces. To see how we build up an interface, we’ll first start by defining only one interface. We’ll define two shapes, a <code>Circle</code> and <code>Square</code>, and they will both define a method called <code>Area</code>. This method will return the geometric area of their respective shapes:

编写Go代码的核心租户之一是编写小的,简洁的类型,然后将它们组合成更大,更复杂的类型。 组成接口时也是如此。 为了了解如何构建接口,我们首先将仅定义一个接口。 我们将定义两个形状,即<code>Circle</code>和<code>Square</code> ,它们都将定义一个称为<code>Area</code>的方法。 此方法将返回其各自形状的几何区域:

main.go
main.go
<pre class="has"><code class="code-highlight language-go">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 }</code></pre>

Because each type declares the <code>Area</code> method, we can create an interface that defines that behavior. We create the following <code>Sizer</code> interface:

因为每种类型都声明<code>Area</code>方法,所以我们可以创建一个定义该行为的接口。 我们创建以下<code>Sizer</code>接口:

main.go
main.go
<pre class="has"><code class="code-highlight language-go">... type Sizer interface { Area() float64 } ...</code></pre>

We then define a function called <code>Less</code> that takes two <code>Sizer</code> and returns the smallest one:

然后,我们定义一个名为<code>Less</code>的函数,该函数需要两个<code>Sizer</code>并返回最小的一个:

main.go
main.go
<pre class="has"><code class="code-highlight language-go">... func Less(s1, s2 Sizer) Sizer { if s1.Area() < s2.Area() { return s1 } return s2 } ...</code></pre>

Notice that we not only accept both arguments as the type <code>Sizer</code>, but we also return the result as a <code>Sizer</code> as well. This means that we no longer return a <code>Square</code> or a <code>Circle</code>, but the interface of <code>Sizer</code>.

请注意,我们不仅将两个参数都接受为<code>Sizer</code>类型,而且还将结果作为<code>Sizer</code>返回。 这意味着我们不再返回<code>Square</code>或<code>Circle</code> ,而是返回<code>Sizer</code>的接口。

Finally, we print out what had the smallest area:

最后,我们打印出面积最小的内容:

<pre class="has"><code>
Output
{Width:5 Height:10} is the smallest </code></pre>

Next, let’s add another behavior to each type. This time we’ll add the <code>String()</code> method that returns a string. This will satisfy the <code>fmt.Stringer</code> interface:

接下来,让我们为每种类型添加另一个行为。 这次,我们将添加返回字符串的<code>String()</code>方法。 这将满足<code>fmt.Stringer</code>接口:

main.go
main.go
<pre class="has"><code>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()) }</code></pre>

Because both the <code>Circle</code> and the <code>Square</code> type implement both the <code>Area</code> and <code>String</code> methods, we can now create another interface to describe that wider set of behavior. To do this, we’ll create an interface called <code>Shaper</code>. We’ll compose this of the <code>Sizer</code> interface and the <code>fmt.Stringer</code> interface:

因为<code>Circle</code>和<code>Square</code>类型都实现了<code>Area</code>和<code>String</code>方法,所以我们现在可以创建另一个接口来描述更广泛的行为。 为此,我们将创建一个名为<code>Shaper</code>的接口。 我们<code>fmt.Stringer</code> <code>Sizer</code>接口和<code>fmt.Stringer</code>接口组成:

main.go
main.go
<pre class="has"><code class="code-highlight language-go">... type Shaper interface { Sizer fmt.Stringer } ...</code></pre>

Note: It is considered idiomatic to try to name your interface by ending in <code>er</code>, such as <code>fmt.Stringer</code>, <code>io.Writer</code>, etc. This is why we named our interface <code>Shaper</code>, and not <code>Shape</code>.

注意:尝试以<code>er</code>结尾(例如<code>fmt.Stringer</code> , <code>io.Writer</code>等)来命名您的接口是惯用的。这就是为什么我们将接口命名为<code>Shaper</code>而不是<code>Shape</code> 。

Now we can create a function called <code>PrintArea</code> that takes a <code>Shaper</code> as an argument. This means that we can call both methods on the passed in value for both the <code>Area</code> and <code>String</code> method:

现在,我们可以创建一个名为<code>PrintArea</code>的函数,该函数将<code>Shaper</code>作为参数。 这意味着我们可以在<code>Area</code>和<code>String</code>方法的传入值上调用这两个方法:

main.go
main.go
<pre class="has"><code class="code-highlight language-go">... func PrintArea(s Shaper) { fmt.Printf("area of %s is %.2f\n", s.String(), s.Area()) }</code></pre>

If we run the program, we will receive the following output:

如果运行程序,将收到以下输出:

<pre class="has"><code>
Output
area of Circle {Radius: 10.00} is 314.16 area of Square {Width: 5.00, Height: 10.00} is 50.00 Square {Width: 5.00, Height: 10.00} is the smallest </code></pre>

We have now seen how we can create smaller interfaces and build them up into larger ones as needed. While we could have started with the larger interface and passed it to all of our functions, it is considered best practice to send only the smallest interface to a function that is needed. This typically results in clearer code, as anything that accepts a specific smaller interface only intends to work with that defined behavior.

现在我们已经看到了如何创建较小的接口,并根据需要将其构建为较大的接口。 虽然我们可以从较大的接口开始并将其传递给我们的所有功能,但最好的做法是仅将最小的接口发送给所需的功能。 通常,这会导致代码更清晰,因为任何接受特定较小接口的内容都仅打算与该定义的行为一起使用。

For example, if we passed <code>Shaper</code> to the <code>Less</code> function, we may assume that it is going to call both the <code>Area</code> and <code>String</code> methods. However, since we only intend to call the <code>Area</code> method, it makes the <code>Less</code> function clear as we know that we can only call the <code>Area</code> method of any argument passed to it.

例如,如果我们将<code>Shaper</code>传递给<code>Less</code>函数,则可以假定它将同时调用<code>Area</code>和<code>String</code>方法。 但是,由于我们只打算调用<code>Area</code>方法,因此它使<code>Less</code>函数变得很清楚,因为我们知道我们只能调用传递给它的任何参数的<code>Area</code>方法。

<h2 id="conclusion"> 结论 <span style="font-weight: bold;">(</span>Conclusion<span style="font-weight: bold;">)</span></h2>

We have seen how creating smaller interfaces and building them up to larger ones allows us to share only what we need to a function or method. We also learned that we can compose our interfaces from other interfaces, including those defined from other packages, not just our packages.

我们已经看到了创建较小的接口并将其构建为较大的接口如何使我们仅与函数或方法共享所需的内容。 我们还了解到,我们可以从其他接口组成我们的接口,包括从其他软件包定义的接口,而不仅仅是我们的软件包。

If you’d like to learn more about the Go programming language, check out the entire How To Code in Go series.

如果您想了解有关Go编程语言的更多信息,请阅读完整的“ 如何在Go中编写代码”系列 。

<blockquote>

翻译自: https://www.digitalocean.com/community/tutorials/how-to-use-interfaces-in-go

</blockquote>

golang接口的使用场景

到此这篇关于“golang接口的使用场景_如何在Go中使用接口”的文章就介绍到这了,更多文章或继续浏览下面的相关文章,希望大家以后多多支持JQ教程网!

您可能感兴趣的文章:
GO接口应用场景说明
Go 接口应用场景、注意事项和使用细节
go语言中的继承和接口使用(八)
19小接口的妙用
2020-10-18Go语言接口
Go语言空接口类型(interface{})
go 获取函数地址_Go语言基础--接口浅析
go语言接口的基本概念
go语言学习-接口赋值的两种类型
go语言学习笔记(十三)——接口类型

[关闭]
~ ~