Why use interfaces?
Let’s look at a simple program. We enter a URL and we output response
import ( "fmt" "io/ioutil" "net/http" ) func retrieve(url string) string{ res, err := http.Get(url) if err ! = nil { panic(err) } defer res.Body.Close() bytes, _ := ioutil.ReadAll(res.Body) return string(bytes) } func main() { fmt.Println(retrieve("https://www.baidu.com")) }Copy the code
There’s no logical problem at all. Is there a better way to write it? After all, the main function is strongly coupled to our url parsing function
Improved version:
package infra import ( "io/ioutil" "net/http" ) type Retriever struct { } func (Retriever) Get(url string) string { res, err := http.Get(url) if err ! = nil { panic(err) } defer res.Body.Close() bytes, _ := ioutil.ReadAll(res.Body) return string(bytes) }Copy the code
package main
import (
"fmt"
"gomodtest/infra"
)
func getRetriever() infra.Retriever{
return infra.Retriever{}
}
func main() {
retriever := getRetriever()
fmt.Println(retriever.Get("https://www.baidu.com"))
}
Copy the code
This looks a lot better than our first version of the program.
So let’s say at this point we’re going to get the test team to do that and they’re going to have a retriever
Since this is the test team, let’s give a fake response string for the time being
package testing
type Retriever struct {
}
func (Retriever) Get(url string) string {
return "fake content"
}
Copy the code
And that’s when you find that your main function is really hard to write and you have to change your getRetriever method inside of main, and you have to change the return value of that method even if you change return. This is when you realize that your code is still coupled
Now, at this point, if you’re coming from Java it’s pretty easy to figure out how to use interfaces.
Just a quick look at how interfaces are used in go
func getRetriever() retriever{
return testing.Retriever{}
}
type retriever interface {
Get(string) string
}
func main() {
retriever := getRetriever()
fmt.Println(retriever.Get("https://www.baidu.com"))
}
Copy the code
It’s very simple, just define an interface and that interface also has a Get method. So for the above requirements
We only need to change the implementation of our return method during testing, but not for external calls
This is the greatest use of the interface, even if it is basically successfully decoupled
duck typing
If you have Java experience in the previous chapter, you may be confused. You define an interface but your implementation does not implement the corresponding interface. This is the difference between the GO interface and Java interface.
Strictly speaking, GO is a structured type system, similar to Duck typing.
Go’s interface describes the external behavior of things rather than the internal structure
Interfaces in the GO language are user-defined
In traditional object-oriented systems such as Java, the interface is generally defined by the provider or the user, which is completely different from THE GO language. In the process of using the GO language, we must pay attention to the change in the way of thinking
Therefore, for the requirements of the previous chapter, we will write the interface of the GO language according to the ideas to achieve one time:
You can see that the Download function uses a retriever. What are the features of this retriever? She has to have a get function
So let’s define one according to this idea:
With this interface, the provider can play around with it, such as a mock service
Caller:
func main() {
fmt.Println(download(mock.Retriever{
Contents: "this is mock",
}))
}
Copy the code
It can be seen that the implementation of interface in GO language is as long as the implementation of the method, so the implementation of interface in GO language is implicit, she does not need to explicitly say which interface I implement, but just implement the method in the interface
This is the biggest interface implementation difference between GO and Java
The value type of the interface
Following the above type we can extend a Website retriever, and then modify our code a little bit to see what the retriver for this interface is
func main() {
var r Retriever
r = mock.Retriever{
Contents: "this is mock",
}
fmt.Printf("%T %v\n",r,r)
fmt.Println(download(r))
r = website.Retriever{
Domain: "baidu",
Protol: "https",
}
fmt.Printf("%T %v\n",r,r)
fmt.Println(download(r))
}
Copy the code
So you can see that r is a value type here, so one might ask, aren’t value types involved in copying can this be a pointer? Of course you can, and it’s easy to modify.
Modify our interface implementation directly:
And then let’s change our interface assignment
Run the program:
You can see that up here
The interface type of go can accept either a value type or a pointer type.
As with Java, we can get the specific type directly:
Here must be careful to write:
func inspect(r Retriever){
switch r.(type) {
case mock.Retriever:
fmt.Println("this is mock retriever")
case *website.Retriever:
fmt.Println("this is website retriever")
}
}
Copy the code
It’s important to remember that interface in GO has two things in it: the implementor’s type and (the implementor’s pointer or value)
** Interface variables come with Pointers **
Interface type {}
This syntax stands for any type
func printAny( r interface{}){
fmt.Println(r)
}
Copy the code
Of course, you can cast from any type
func addAny( r interface{},q interface{}) int{
return r.(int)+q.(int)
}
Copy the code
However, this method does not detect errors at compile time, but only at run time.
Combination of interfaces
This is one of the differences between go and the Java language, where interfaces and interfaces cannot be combined. This is going to cause us some problems at some point.
For example, if you write a function that takes an Interface Jump as an argument in Java, if you want the function to take an interface Run as an argument, there’s no way to do that.
But in GO, it’s ok
For instance
type Jump interface {
JumpIn()
}
type Run interface {
RunIn()
}
type RunAndJump interface {
Jump
Run
Happy()
}
Copy the code
In addition to defining RunAndJump, we’ve also defined RunAndJump which has RunAndJump properties and a happy function
Let’s define another function
func printInfo(rq RunAndJump){
rq.RunIn()
rq.JumpIn()
rq.Happy()
}
Copy the code
Notice that the argument becomes this interface type
Now let’s define a structure that implements all three of these methods to be of type RunAndJump
package test
import "fmt"
type Person struct {
Name string
}
func (q Person) JumpIn() {
fmt.Println(q.Name + " jump")
}
func (q Person) RunIn() {
fmt.Println(q.Name + " run")
}
func (q Person) Happy() {
fmt.Println(q.Name + " is happy")
}
Copy the code
Finally, the main function
func main() {
printInfo(test.Person{
Name: "wuyue",
})
}
Copy the code
Finally, go provides many useful interfaces in library functions such as Reader Writer and Stringer
If you are interested in looking at these interfaces and implementing the corresponding methods in your own code, you will be able to adapt many of the basic functions provided by the GO language.