Effective Go-4

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 from Pexels

https://golang.org/doc/effective_go

Comments are closed.