go系列知识整理之interface
在go的源代码中,gomicro源码,etcd源码,随处可见的这种写法,
需要扩展的时候,可以重新实现一个接口,只能函数名与之保持一致,就会被编译器识别
package main
import (
"encoding/json"
"fmt"
"reflect"
"testing"
"time"
)
//Animal 类型是一个接口,我们将定义一个 Animal 作为任何可以说话的东西
//这是 Go 类型系统的核心概念:我们根据类型可以执行的操作而不是其所能容纳的数据类型来设计抽象
//我们定义 Animal 为任何具有 Speak 方法的类型
//所有定义了该方法的类型我们称它实现了 Animal 接口
//Go 中没有 implements 关键字,判断一个类型是否实现了一个接口是完全是自动地
type Animal interface {
Speak() string
}
type Dog struct {
}
func (d Dog) Speak() string {
return "Woof!"
}
type Cat struct {
}
func (c Cat) Speak() string {
return "Meow!"
}
type CatPoint struct {
}
func (c *CatPoint) Speak() string {
return "Point Meow!"
}
func TestInterface(t *testing.T) {
// animals := []Animal{Dog{}, Cat{},&CatPoint{},new(CatPoint)}
//&Dog{} 与 CatPoint{}: 一个指针类型可以通过其相关的值类型来访问值类型的方法,但是反过来不行
//因为所有的参数都是通过值传递的,这就可以解释为什么 *Cat 的方法不能被 Cat 类型的值调用了。
//任何一个 Cat 类型的值可能会有很多 *Cat 类型的指针指向它,如果我们尝试通过 Cat 类型的值来调用 *Cat 的方法,根本就不知道对应的是哪个指针
animals := []Animal{&Dog{}, Cat{}, &CatPoint{}, new(CatPoint)}
for _, animal := range animals {
fmt.Println(animal.Speak())
}
}
重写Timestamp的格式化时间方法
var input = `
{
"created_at": "Thu May 31 00:00:01 +0000 2012"
}
`
var input1 = `"Thu May 31 00:00:01 +0000 2012"`
type Unmarshaler interface {
UnmarshalJSON([]byte) error
}
type Timestamp time.Time
func TestTimestamp(t *testing.T) {
// func main(){
// our target will be of type map[string]interface{}, which is a
// pretty generic type that will give us a hashtable whose keys
// are strings, and whose values are of type interface{}
var val map[string]interface{}
if err := json.Unmarshal([]byte(input), &val); err != nil {
panic(err)
}
fmt.Println(val)
for k, v := range val {
fmt.Println(k, reflect.TypeOf(v))
}
// map[created_at:Thu May 31 00:00:01 +0000 2012]
// created_at string
//转换为时间格式
t1 := Timestamp{}
if err := t1.UnmarshalJSON([]byte(input1)); err != nil {
panic(err)
}
fmt.Println(t1)
// for k, v := range val {
// fmt.Println(k, reflect.TypeOf(v))
// }
}
func (t *Timestamp) UnmarshalJSON(b []byte) error {
v, err := time.Parse(time.RubyDate, string(b[1:len(b)-1]))
if err != nil {
return err
}
*t = Timestamp(v)
return nil
}
代码参考: https://www.jianshu.com/p/88c4ed564aa9
上面写了这么多,最终其实是想解决一个问题,初看下面这段etcd源代码时,没看懂,然后就去找资料
func (t *Transport) Handler() http.Handler {
//创建pipelineHandler、streamHandler 、snapHandler 三个实例,这三个实例都实现了Handler接口
pipelineHandler := newPipelineHandler(t, t.Raft, t.ClusterID)
streamHandler := newStreamHandler(t, t, t.Raft, t.ID, t.ClusterID)
snapHandler := newSnapshotHandler(t, t.Raft, t.Snapshotter, t.ClusterID)
mux := http.NewServeMux()//mux是多路复用器 ServeMux主要通过m字段(map[string]muxEntry)存储URL和Handler实例之间的映射关系,设置URL和Handler之间的对应关系
mux.Handle(RaftPrefix, pipelineHandler)
mux.Handle(RaftStreamPrefix+"/", streamHandler)
mux.Handle(RaftSnapshotPrefix, snapHandler)
mux.Handle(ProbingPrefix, probing.NewHandler())
return mux
}
又跑了下面这些例子
package main
import (
"log"
"net/http"
"testing"
"time"
)
func TestHttp1(t *testing.T) {
mux := http.NewServeMux()
rh := http.RedirectHandler("http://example.org", 307)
mux.Handle("/foo", rh)
log.Println("Listening...")
http.ListenAndServe(":3000", mux)
}
func timeHandler(w http.ResponseWriter, r *http.Request) {
tm := time.Now().Format(time.RFC1123)
w.Write([]byte("The time is: " + tm))
}
func method1() {
// DefaultServeMux
// DefaultServeMux 是一种ServeMux,它会随着 net/http 包初始化被自动初始化。
//net/http 包提供了一组快捷函数(http.Handle 和 http.HandleFunc)来配合 DefaultServeMux,
//这些函数将处理器注册到 DefaultServerMux。
//当ListenAndServe在没有提供其他的处理器的情况下(也就是第二个参数设成了 nil),内部会使用 DefaultServeMux
// Note that we skip creating the ServeMux...
th := http.HandlerFunc(timeHandler)
// We use http.Handle instead of mux.Handle...
http.Handle("/time", th)
log.Println("Listening...")
// And pass nil as the handler to ListenAndServe.
http.ListenAndServe(":8488", nil)
}
func method2() {
mux := http.NewServeMux()
mux.HandleFunc("/time", timeHandler)
log.Println("Listening...")
http.ListenAndServe(":3000", mux)
}
func timeHandler1(format string) http.Handler {
//首先是创建了一个fn,这是个匿名函数,将 format 变量封装到一个闭包里。
//闭包的本质让处理器在任何情况下,都可以在本地范围内访问到 format 变量
fn := func(w http.ResponseWriter, r *http.Request) {
tm := time.Now().Format(format)
w.Write([]byte("The time is: " + tm))
}
//其次我们的闭包函数满足 func(http.ResponseWriter, *http.Request) 签名。
//如果你记得之前我们说的,这意味我们可以将它转换成一个HandlerFunc类型(满足了http.Handler接口)。
//我们的timeHandler 函数随后转换后的 HandlerFunc 返回
return http.HandlerFunc(fn)
}
func method3() {
mux := http.NewServeMux()
th := timeHandler1(time.RFC1123) //主函数带有传参
mux.Handle("/time", th)
log.Println("Listening...")
http.ListenAndServe(":3000", mux)
}
type timeHandlerDefine struct {
format string
}
func (th *timeHandlerDefine) ServeHTTP(w http.ResponseWriter, r *http.Request) {
tm := time.Now().Format(th.format)
w.Write([]byte("The time is: " + tm))
}
//method4函数中,我们像初始化一个常规的结构体一样,初始化了timeHandler,用 & 符号获得了其地址。
//随后,像之前的例子一样,我们使用 mux.Handle 函数来将其注册到 ServerMux
func method4() {
mux := http.NewServeMux()
th := &timeHandlerDefine{format: time.RFC1123}
mux.Handle("/time", th)
//复用
th1123 := &timeHandlerDefine{format: time.RFC1123}
mux.Handle("/time/rfc1123", th1123)
th3339 := &timeHandlerDefine{format: time.RFC3339}
mux.Handle("/time/rfc3339", th3339)
log.Println("Listening...")
http.ListenAndServe(":3000", mux)
}
// 类似如下代码:
// func (t *Transport) Handler() http.Handler {
// //创建pipelineHandler、streamHandler 、snapHandler 三个实例,这三个实例都实现了Handler接口
// pipelineHandler := newPipelineHandler(t, t.Raft, t.ClusterID)
// streamHandler := newStreamHandler(t, t, t.Raft, t.ID, t.ClusterID)
// snapHandler := newSnapshotHandler(t, t.Raft, t.Snapshotter, t.ClusterID)
// mux := http.NewServeMux()//mux是多路复用器 ServeMux主要通过m字段(map[string]muxEntry)存储URL和Handler实例之间的映射关系,设置URL和Handler之间的对应关系
// mux.Handle(RaftPrefix, pipelineHandler)
// mux.Handle(RaftStreamPrefix+"/", streamHandler)
// mux.Handle(RaftSnapshotPrefix, snapHandler)
// mux.Handle(ProbingPrefix, probing.NewHandler())
// return mux
// }
func main() {
method1()//需要有标准的ServeHTTP函数实现,任何有 func(http.ResponseWriter, *http.Request) 签名的函数都能转化为一个 HandlerFunc 类型
// method2()
// method3()
// method4()
}