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