# Effective Go-4
Date: 2021-04-19


#### **Interfaces and other types**

##### **Interface**

Sequence这个例子，调用sort package中的方法，对于集合要实现sort的接口：Len(), Less(i,j int) bool 和 Swap(i,j int)。Sequence同时通过String()对集合内容进行格式化。

```
package sequence_test

import (
    "fmt"
    "sort"
    "testing"
)

type Sequence []int

func (s Sequence) Len() int {
    return len(s)
}

func (s Sequence) Less(i, j int) bool {
    return s[i] < s[j]
}

func (s Sequence) Swap(i, j int) {
    s[i], s[j] = s[j], s[i]
}

func (s Sequence) Copy() Sequence {
    copy := make(Sequence, 0, len(s))
    return append(copy, s...)
}

func (s Sequence) String() string {
    s = s.Copy()
    sort.Sort(s)
    str := "["
    for i, elem := range s {
        if i > 0 {
            str += " "
        }
        str += fmt.Sprint(elem)
    }
    return str + "]"
}

func (s Sequence) Stringsprint() string {
    s = s.Copy()
    sort.Sort(s)
    return fmt.Sprint(s)
}

func (s Sequence) StringIntSlice() string {
    s = s.Copy()
    sort.IntSlice(s).Sort()
    return fmt.Sprint(s)
}
func TestSequence(t *testing.T) {
    var s Sequence = Sequence{100, 12, 83, 34, 59, 86}
    t.Logf("%#v", s)
    t.Log(s)
    t.Log(s.String())
    t.Log(s.Stringsprint())
    t.Log(s.StringIntSlice())
}

```

```
=== RUN   TestSequence
    f:\GO\go_test\sequence\sequence_test.go:54: sequence_test.Sequence{100, 12, 83, 34, 59, 86}
    f:\GO\go_test\sequence\sequence_test.go:55: [12 34 59 83 86 100]
    f:\GO\go_test\sequence\sequence_test.go:56: [12 34 59 83 86 100]
    f:\GO\go_test\sequence\sequence_test.go:57: [12 34 59 83 86 100]
    f:\GO\go_test\sequence\sequence_test.go:58: [12 34 59 83 86 100]
--- PASS: TestSequence (0.00s)
PASS
ok      loop/sequence   1.996s

```

##### **类型转换**

String()接口通过 “for i, elem := range”方式实现，也可以像Stringsprint直接调用fmt.Sprint处理实现。

由于要通过sort.Sort(slice)实现排序， slice这里就是Sequence只要实现sort三个接口Less， Swap， Len就可以。通过IntSlice(s)将 Sequence 转化为 IntSlice类型，则不需要实现三个接口。

Sequence 和\[\]int类型转化，其实他们是一样的， 不会产生新值，如果从整形到浮点型需要创建一个新值。

##### **Interface conversions and type assertions**

Type Switch使用接口方式判断一个值的类型。

```
package conversion_test

import (
    "testing"
)

type Stringer interface {
    String() string
}

type GoStringer struct{}

func (p *GoStringer) String() string {
    return "Helloworld"
}

func Gettype() string {
    var value interface {
    }
    var s Stringer = &GoStringer{}
    value = s
    switch str := value.(type) {
    case string:
        return str
    case Stringer:
        return str.String()
    }
    return "?"
}

func TestConversion(t *testing.T) {
    t.Log(Gettype())
}

```

也可以判断其是否某一值

```
package check_test

import "testing"

func TestCheck(t *testing.T) {
    var value interface{}
    value = "123"
    value = 123
    str, ok := value.(string)
    if ok {
        t.Logf("string value is %q\n", str)
    } else {
        t.Logf("value is not a string\n")
    }
}

```

 

##### **Generality**

泛型， 通过泛型使得NewCTR并不依赖加密算法和具体的数据源格式。也就是基于抽象接口的编程。

```
type Block interface {
    BlockSize() int
    Encrypt(dst, src []byte)
    Decrypt(dst, src []byte)
}

type Stream interface {
    XORKeyStream(dst, src []byte)
}

// NewCTR returns a Stream that encrypts/decrypts using the given Block in
// counter mode. The length of iv must be the same as the Block's block size.
func NewCTR(block Block, iv []byte) Stream
```

##### **Interfaces and methods 接口方法**

以net/http库中接口为例。

```
type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}
```

下面通过顶一个counter类型记录网页访问，次数，也可以通过管道进行通知（这里直接过滤掉了），另外也可以通过http.HandlerFunc来指定收到http请求后的处理函数。

```
package http_test

import (
    "fmt"
    "net/http"
    "os"
    "testing"
)

/* in net/http
type handler interface {
  ServeHTTP(ResponseWriter, *Request)
}
*/

type Counter struct {
    n int
}

type Chan chan *http.Request

func (ctr *Counter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    ctr.n++
    fmt.Fprintf(w, "counter = %d\n", ctr.n)
}

func (ch Chan) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    ch <- req
    defer func() { <-ch }()
    fmt.Fprintf(w, "notification sent\n")
}

func ArgServer(w http.ResponseWriter, req *http.Request) {
    fmt.Fprintf(w, "%s", os.Args)
}

func TestCounter(t *testing.T) {
    ctr := new(Counter)
    http.Handle("/counter", ctr)

    ch := make(Chan, 1)
    http.Handle("/chan", ch)

    http.Handle("/args", http.HandlerFunc(ArgServer))
    http.ListenAndServe(":8090", nil)
}
```

可以看到由于http.Handle第二个参数是接口类型， 他是可以支持结构， 整型，函数类型，channel的。

 

#### **The blank identifier**

the blank identifier 类似占位符， 对于赋值操作类似unix中的/dev/null

主要用以以下场景：

- 多赋值场景

```
package blank_test

import (
    "os"
    "testing"
)

func TestBlank(t *testing.T) {
    path := "/usr/local"
    if _, err := os.Stat(path); os.IsNotExist(err) {
        t.Logf("%s does not exist \n", path)
    }
}
```

- 未使用的导入和变量，由于go定义的变量或引入包必须使用，可以通过“\_”变量的使用的前期开发中可以不必删除开始还没有使用的变量或包，可使程序编译。

```
package unused_test

import (
    "io"
    "testing"
    "fmt"
    "log"
    "os"
)

func TestUnused(t *testing.T) {
  var _ io.Reader
  var _ = fmt.Print
  var _ log.Logger

  fd, err := os.Open("test.go")
  if err != nil {
      t.Log(err)
  }
  _ = fd
}
```

- 由于辅助作用导入

```
import _ "net/http/pprof"
```

- 接口检查

如果需要判断 json.RawMessage是否实现了json.Marshaler ， 这样可以在编译阶段进行接口实现的检查

```
var _ json.Marshaler = (*RawMessage)(nil)
```

 

#### **Embedding**

使用组合而不是继承， 在struct和interface中可以实现组合。 接口只能内嵌接口类型。

与继承不同，组合嵌入一类型后内部类型的方法可以供外部类型调用，但是他们的接收方法是属于内部类型的。

```
// ReadWriter stores pointers to a Reader and a Writer.
// It implements io.ReadWriter.
type ReadWriter struct {
    *Reader  // *bufio.Reader
    *Writer  // *bufio.Writer
}

```

```
func (rw *ReadWriter) Read(p []byte) (n int, err error) {
    return rw.reader.Read(p)
}
```

ReadWriter中的Reader， 内嵌了io.Reader, Writer也同样内嵌了io.Writer

```
// Reader implements buffering for an io.Reader object.
type Reader struct {
    buf          []byte
    rd           io.Reader // reader provided by the client
    r, w         int       // buf read and write positions
    err          error
    lastByte     int // last byte read for UnreadByte; -1 means invalid
    lastRuneSize int // size of last rune read for UnreadRune; -1 means invalid
}
type Writer struct {
    err error
    buf []byte
    n   int
    wr  io.Writer
}
```

通过组合模式进行定制化输出。

```
package embed_test

import (
    "fmt"
    "log"
    "os"
    "testing"
)

type Job struct {
    Command string
    *log.Logger
}

func NewJob(command string, logger *log.Logger) *Job {
    return &Job{command, logger}
}

func (job *Job) Printf(format string, args ...interface{}) {
    job.Logger.Printf("%s: %s", job.Command, fmt.Sprintf(format, args...))
}

func TestJob(t *testing.T) {
    job := NewJob("CreateJob", log.New(os.Stderr, "Job ", log.Ldate))
    job.Println("TestJob")
    job.Printf("%s\n", "TestJob")
}

```

 

 

 

#### **参考及引用**

Photo by **[Johannes Plenio](https://www.pexels.com/@jplenio?utm_content=attributionCopyText&utm_medium=referral&utm_source=pexels)** from **[Pexels](https://www.pexels.com/photo/blue-body-of-water-with-orange-thunder-1102915/?utm_content=attributionCopyText&utm_medium=referral&utm_source=pexels)**

https://golang.org/doc/effective\_go

