This is the 20th day of my participation in the August More Text Challenge
Combination of interfaces
Interface composition is a combination of interfaces, sort of like multiple inheritance. You can just look at the example below
type Retriver interface {
Get(url string) string
}
type Poster interface {
Post(url string, form map[string]string) string
}
func download(r Retriver) string {
return r.Get("<http://www.baidu.com>")
}
func post(poster Poster) {
poster.Post("<http://ww.baidu.com>", map[string]string{
"name": "shulv",
"age": "twenty",
})
}
Copy the code
There are two interfaces defined above, and there are callers for them, Download and POST. Now suppose you have a session function that takes one parameter, a Retriver, and a Poster. This is where the combination of interfaces comes in
Type RetrieverPoster interface {Retriver Poster //Connect(host string) // You can also add other methods} func session(s RetrieverPoster) string { s.Post("<http://www.baidu.com>", map[string]string{ "Contents": "facked", }) return s.Get("<http://www.baidu.com>") }Copy the code
The above code defines a combined interface RetrieverPoster that directly includes the Retriver and Poster interfaces, meaning that he can use the methods from the two interfaces directly. Of course, you can add your own methods to the composite interface. Do you notice that it looks very similar to structure nesting?
For ease of understanding, I define the structure directly in a file and implement some methods of the structure
type Mock struct { Contains string } func (r Mock) Get(url string) string { return r.Contains } func (r *Mock) Post(url string, form map[string]string) string { r.Contains = form["Contents"] return "ok" } type Real struct { UserAgent string TimeOut time.Duration } func (r Real) Get(url string) string { resp, err := http.Get(url) if err ! = nil { panic(err) } result, err := httputil.DumpResponse(resp, true) resp.Body.Close() if err ! = nil { panic(err) } return string(result) }Copy the code
The above defines two constructs, Mock and Real, and implements the corresponding methods (as you can see, Mock implements the combined interface RetrieverPoster). Call the session function in main
Func main() {retriver := Mock{"just a test"} fmt.println (session(&retriever))} Facked (because Mock.Content was modified in the Post method to indicate that the Mock Post method was called in the session)Copy the code
In fact, in the Go language library, there are many examples of interface combination, such as ReadWriteCloser in IO package, which is an interface composed of multiple interfaces, the content is as follows:
// ReadWriteCloser is the interface that groups the basic Read, Write and Close methods.
type ReadWriteCloser interface {
Reader
Writer
Closer
}
Copy the code
You can see that it is a combination of the Reader, Writer, and Closer interfaces. Of course, you can go into the IO package, which has a lot of these combined interfaces
This is the combination of interfaces, but below we share some of the standard interfaces in Go
Common System Interfaces
Stringer
In the FMT package, there is an interface called Stringer, which contains only a String function, so a type that implements the String function implements the Stringer interface
package fmt
type Stringer interface {
String() string
}
Copy the code
Let’s define a structure type that implements the String method
package main import "fmt" type Mock struct { Contents string } func (m Mock) String() string { return fmt.Sprintf("Mock: {Contents = %s}", m.contents)} func main() {m := Mock{"a test, too"} fmt.println (m.tring ())} {Contents = a test, too}Copy the code
Reader
The IO package has a Reader interface, which has only one Read interface. The Read method can be implemented as a file, Read from the file, and put into byte (not only file, but also network, slice, etc.).
package io
type Reader interface {
Read(p []byte) (n int, err error)
}
Copy the code
If you look at the internal implementation of the os.open method, its return value is a structure of File, and if you go into File, you can see that it is a structure type, and you can see that the structure has a structure of File embedded in it, which is actually the actual representation of *File. *File implements the Read method, so it actually implements the Reader interface
//file.go package os func Open(name string) (*File, error) { return OpenFile(name, O_RDONLY, 0) } //types.go package os type File struct { *file // os specific } .... // file is the real representation of *File. type file struct { pfd poll.FD name string dirinfo *dirInfo // nil unless directory being read nonblock bool // whether we set nonblocking mode stdoutOrErr bool // whether this is stdout or Stderr appendMode bool // Whether file is opened for appending} //file.go package os func (f *File) Read(b []byte) (n int, err error) { if err := f.checkValid("read"); err ! = nil { return 0, err } n, e := f.read(b) return n, f.wrapErr("read", e) }Copy the code
As you can see in *File, it doesn’t say which interfaces I implemented. It just needs to implement certain methods
So we call bufio.newscanner () when we get the contents of the File. It takes an io.reader as an argument.
filename := "a.txt" file, Err := os.open (filename) scanner := bufio.newscanner (file) for scanner.scan () {fmt.println (scanner.text ())// Reads the contents of the file line by line Func NewScanner(r io.reader) *Scanner {return &Scanner{r: r, split: ScanLines, maxTokenSize: MaxScanTokenSize, } }Copy the code
Writer
Like the Reader interface, it is in the IO package, and the interface has only one Write method. The Write method can be implemented as a file to which supplied byte data is written
package io
type Writer interface {
Write(p []byte) (n int, err error)
}
Copy the code
The FMT package contains a function called Fprintf, which returns a formatted string. Its first argument is an io.Writer type, so anything that implements the Writer interface can be passed to it
func Fprintf(w io.Writer, format string, a ... interface{}) (n int, err error) { p := newPrinter() p.doPrintf(format, a) n, err = w.Write(p.buf) p.free() return }Copy the code
Example: Reading file contents
Take reading file contents as an example
package main import ( "bufio" "fmt" "os" ) func printFile(filename string) { file, err := os.Open(filename) if err ! = nil {panic(err)} scanner := bufio.newscanner (file) for scanner.scan () {fmt.println (scanner.text ())// Read the contents of the file line by line}} func main() { printFile("a.txt") }Copy the code
The downside of this approach is that printFile can only accept a filename string and can only read the contents of the file. The following optimizes it so that it can print not only the contents of the file, but also the string by printing it as if it were a file
package main import ( "bufio" "fmt" "io" "os" "strings" ) func printFile(filename string) { file, err := os.Open(filename) if err ! = nil { panic(err) } printFileContents(file) } func printFileContents(read io.Reader) { scanner := Bufio.newscanner (read) for scanner.scan () {fmt.println (scanner.text ())// Read the contents of the file line by line}} func main() {printFile("a.txt") s := `uiieo "dgsjg" s d 3 ` printFileContents(strings.NewReader(s)) }Copy the code
You can see that leaving the actual printing to the printFileContents method, which takes a Reader argument, so you can use it to print the file, as well as the string type as a file, makes it more generic