Golang实践:interface
接口类型是对其他类型行为的概括与抽象; 通过使用接口,我们可以写出更加灵活和通用的函数,这些函数不用绑定在一个特定的类型实现上;
接口即约定
接口是一种抽象类型,它并没有暴露所含数据的布局或者内部结构;它所提供的仅是一些方法而已;
接口类型
一个接口类型定义了一套方法,如果一个具体类型要实现该接口,那么必须实现接口类型定义中的所有方法;
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
type Closer interface {
Close() error
}
// ReadWriter is the interface that groups the basic Read and Write methods.
type ReadWriter interface {
Reader
Writer
}
// ReadWriteCloser is the interface that groups the basic Read, Write and Close methods.
type ReadWriteCloser interface {
Reader
Writer
Closer
}
实现接口
如果一个类型实现了一个接口要求的所有方法,那么这个类型实现了这个接口;
使用flag.Value来解析参数
type Value interface {
String() string
Set(string) error
}
package main
import (
"flag"
"fmt"
"time"
)
var period = flag.Duration("period", 1*time.Second, "sleep period")
func main() {
flag.Parse()
fmt.Printf("Sleeping for %v...", *period)
time.Sleep(*period)
}
go run xxx.go -period=”1s” Sleeping for 1s…
自定义flag.Value
package main
import (
"flag"
"fmt"
"strings"
"time"
)
type MyStruct struct {
ID int
Name string
}
func (m *MyStruct)String() string{
return fmt.Sprintf("id: %d, name: %s.", m.ID, m.Name)
}
type myFlag struct {
MyStruct
}
func (f *myFlag) Set(s string) error{
var id int
var name string
fmt.Sscanf(s, "%d%s", &id, &name)
f.Name = name
f.ID = id
return nil
}
func MyFlag(name string, value MyStruct, usage string) *MyStruct{
m := myFlag{value}
flag.CommandLine.Var(&m, name, usage)
return &m.MyStruct
}
var testVal = MyFlag("test", MyStruct{123, "test_name"}, "test")
func main() {
flag.Parse()
fmt.Println(testVal)
}
接口值
一个接口类型的值(接口值)包含两个部分:一个具体类型和该类型的一个值; 二者称为接口的动态类型和动态值;
使用sort.Interface来排序
package sort
// Package sort provides primitives for sorting slices and user-defined
// collections.
package sort
// A type, typically a collection, that satisfies sort.Interface can be
// sorted by the routines in this package. The methods require that the
// elements of the collection be enumerated by an integer index.
type Interface interface {
// Len is the number of elements in the collection.
Len() int
// Less reports whether the element with
// index i should sort before the element with index j.
Less(i, j int) bool
// Swap swaps the elements with indexes i and j.
Swap(i, j int)
}
// Sort sorts data.
// It makes one call to data.Len to determine n, and O(n*log(n)) calls to
// data.Less and data.Swap. The sort is not guaranteed to be stable.
func Sort(data Interface) {
n := data.Len()
quickSort(data, 0, n, maxDepth(n))
}
how to sort.Sort()
type IntSlice []int
func (p IntSlice) Len() int { return len(p) }
func (p IntSlice) Less(i, j int) bool { return p[i] < p[j] }
func (p IntSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func Sort() {
arr := []int{1,9,1}
sort.Sort(IntSlice(arr))
fmt.Println(arr)
}
sort.Ints(arr):
// Ints sorts a slice of ints in increasing order. func Ints(a []int) { Sort(IntSlice(a)) }
http.Handler接口
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as HTTP handlers. If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler that calls f.
type HandlerFunc func(ResponseWriter, *Request)
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
示例:
mux := http.NewServeMux()
mux.HandleFunc("/", ServerHandle)
mux.HandleFunc("/1", ServerHandle1)
fmt.Println("Start server....")
http.ListenAndServe("", mux)
http server package:
type ServeMux struct {
mu sync.RWMutex
m map[string]muxEntry
es []muxEntry // slice of entries sorted from longest to shortest.
hosts bool // whether any patterns contain hostnames
}
type muxEntry struct {
h Handler
pattern string
}
// NewServeMux allocates and returns a new ServeMux.
func NewServeMux() *ServeMux { return new(ServeMux) }
// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux
error接口
type error interface {
Error() string
}
errors package:
package errors
// New returns an error that formats as the given text.
// Each call to New returns a distinct error value even if the text is identical.
func New(text string) error {
return &errorString{text}
}
// errorString is a trivial implementation of error.
type errorString struct {
s string
}
func (e *errorString) Error() string {
return e.s
}
类型断言
类型断言是一个作用在接口值上的操作,写出来类似于x.(T); x是一个接口类型的表达式,T是一个类型(断言类型); 类型断言会检查作为操作数的动态类型是否满足指定的断言类型;
类型断言就是用来从它的操作数中把具体类型的值提取出来的操作;如果检查失败,那么操作崩溃;
断言类型是一个接口值表达式,从一个接口类型变为拥有另外一套方法的接口类型,但保留了接口值中的动态类型和动态值部分;
无论哪种类型作为断言类型,如果操作数是一个空接口值,类型断言都失败;
如果类型断言出现在需要两个结果的赋值表达式,那么断言不会在失败实崩溃; 而是多返回一个布尔型的返回值来指示断言是否成功;
var w io.Writer = os.Stdout
ff, ok := w.(*os.File)
bb, ok := w.(*bytes.Buffer)
使用类型断言来识别错误
// PathError records an error and the operation and file path that caused it.
type PathError struct {
Op string
Path string
Err error
}
func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error() }
func (e *PathError) Unwrap() error { return e.Err }
使用类型断言
func IsNotExist(err error) bool {
if pe, ok := err.(*os.PathError); ok {
err = pe.Err
}
return err == syscall.ENOENT
}
_, err := os.Open("xxx")
fmt.Println(os.IsNotExist(err))
通过接口类型断言来查询特性
func helloworld(w http.ResponseWriter, r *http.Request) { // w.Write([]byte(“hello world\n”)) io.WriteString(w, “hello world\n”) }
// StringWriter is the interface that wraps the WriteString method.
type StringWriter interface {
WriteString(s string) (n int, err error)
}
// WriteString writes the contents of the string s to w, which accepts a slice of bytes.
// If w implements StringWriter, its WriteString method is invoked directly.
// Otherwise, w.Write is called exactly once.
func WriteString(w Writer, s string) (n int, err error) {
if sw, ok := w.(StringWriter); ok {
return sw.WriteString(s)
}
return w.Write([]byte(s))
}
类型分支
func GetInterfaceType(x interface{}) string{
switch x := x.(type) {
case nil:
return "NULL"
case int, uint:
return fmt.Sprintf("%d", x)
case bool:
if x{
return "TRUE"
}
return "FALSE"
case string:
return x
default:
panic(fmt.Sprintf("unexpected type: %T: %v", x, x))
}
}
一些建议
可以用导出机制来限制一个类型的哪些方法或者结构体的哪些字段是对包外可见的; 仅在两个或者多个具体类型需要按统一的方式处理时才需要接口;
设计新类型时越小的接口越容易满足;一个不错的接口设计经验是仅要求你需要的;
Go语言能很好的支持面向对象编程风格,但不是所有东西都必须是一个对象; 全局函数应该有它们的位置,不完全封装的数据类型也应该有位置;
go版本
go version go1.13.6 darwin/amd64