go基础7-常用接口及应用

常用接口

  • sort.Interface

sort包是go内置排序函数包.go的排序函数只是接口声明,不对具体类型和元素做假设.它通过sort.Interface接口约定具体排序序列,它需要知道三个参数长度,比较结果,交换方式.

type Interface interface {
    Len() int
    Less(i, j int) bool // i, j are indices of sequence elements
    Swap(i, j int)
}
  • http.Handler

请求处理接口

package http

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

func ListenAndServe(address string, h Handler) error

go的net/http包实现网络客户端,需要通过ListenAndServe监听服务地址及通过Handler进行请求分发.

package http

type HandlerFunc func(w ResponseWriter, r *Request)

func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}

HandlerFunc是实现Handler接口的函数类型.它通过ServeHTTP方法调用函数本身,来进行handler接口适配.

db := database{"shoes": 50, "socks": 5}
http.HandleFunc("/list", db.list)
http.HandleFunc("/price", db.price)
log.Fatal(http.ListenAndServe("localhost:8000", nil))

// package http
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	DefaultServeMux.HandleFunc(pattern, handler)
}

func (db database) list(w http.ResponseWriter, req *http.Request) {
    for item, price := range db {
        fmt.Fprintf(w, "%s: %s\n", item, price)
    }
}

http.HandleFunc需要一个路径和一个handler函数,database实现了Handler接口,同时它又使用HandlerFunc适配器模式,来定义list方法进行方法适配.

  • error接口

error用于返回异常信息,实际它只是一个接口.

type error interface {
    Error() string
}

使用:

  1. errors.new函数.传入错误信息,返回一个新的error
  2. fmt.Errorf函数.封装了errors.New函数,并可以进行异常信息格式化
  3. syscall.Error.它提供了go语言底层系统的错误信息
  • 类型断言

类型断言是接口值类型匹配判断.语法:x.(T);x表示接口类型,T表示类型;类型断言检查接口值x的动态类型是否和断言类型T匹配.

匹配结果:

1 T是具体类型.它会检查x的动态类型是否和T相同.成功则类型断言的结果是x的动态值(类型是T).失败抛出panic;

var w io.Writer
w = os.Stdout
f := w.(*os.File)      // success: f == os.Stdout
c := w.(*bytes.Buffer) // panic: interface holds *os.File, not *bytes.Buffer

2 T是接口类型.它会检查x的动态来信是否满足T.成功则类型断言的结果是x的动态值(类型是T).失败抛出panic;区别就是前者x的类型是结果类型最大集,后者是T是类型的最大集.

var w io.Writer
w = os.Stdout
rw := w.(io.ReadWriter) // success: *os.File has both Read and Write
w = new(ByteCounter)
rw = w.(io.ReadWriter) // panic: *ByteCounter has no Read method

nil接口值的类型断言都是失败.针对T是接口类型,我们不需要针对T包含x的情形做断言,因为它类似于一次赋值操作.nil接口值例外.

var w io.Writer
var rw io.ReadWriter
w = rw
w = rw.(io.Writer) // fails only if rw == nil

针对接口值动态类型不确定的情形,通常会检验它是否是特定类型.并通过额外结果来判断类型是否相同.

if f, ok := w.(*os.File); ok {
    // ...use f...
}

这里f是结果值,ok是类型是否相同的boolean值,如果ok=true则进入判断方法体内.

类型断言的应用:

1 错误类型判断.
2 询问行为.通过类型断言来判断是否存在特定行为,以满足个性化和统一处理的平衡.

func formatOneValue(x interface{}) string {    
    if err, ok := x.(error); ok {    
        return err.Error()    
    }
    // ...all other types...    
}    
  • 接口的应用
  1. 声明方法.表达接口和实现间的相似性,隐藏实现细节;重点在于方法,而不是具体类型实现.
  2. 值传递.接口值可以是任意类型,通过类型断言来动态区分类型与处理.该方式重点在于类型处理,而不在于接口的方法.因为没有隐藏细节所以也成为可辨识联合.

值传递示例:

func sqlQuote(x interface{}) string {
    switch x := x.(type) {
    case nil:
        return "NULL"
    case int, uint:
        return fmt.Sprintf("%d", x) // x has type interface{} here.
    case bool:
        if x {
            return "TRUE"
        }
        return "FALSE"
    case string:
        return sqlQuoteString(x) // (not shown)
    default:
        panic(fmt.Sprintf("unexpected type %T: %v", x, x))
    }
}

通过接口值interface{}来接收任意参数,类型断言x := x.(type)来判断具体参数类型,然后通过switch来进行处理筛选,定位到具体实现方法;

使用原则:

  1. 两个及多个具体类型进行相同处理时才需要接口抽象,如果只有一个具体类抽象没有意义;
  2. 不同包中接口可以用来进行解耦;
  3. 接口定义应该在满足需求的前提下,尽可能小而简单,这样更利于使用;
  4. 对象和封装并不总是必须的,要分析具体使用情形;
posted @ 2020-08-30 16:56  橙木鱼  阅读(298)  评论(0编辑  收藏  举报