Golang实践:interface

发布于 2017-09-04 · 本文总共 5609 字 · 阅读大约需要 17 分钟

接口类型是对其他类型行为的概括与抽象; 通过使用接口,我们可以写出更加灵活和通用的函数,这些函数不用绑定在一个特定的类型实现上;

接口即约定

接口是一种抽象类型,它并没有暴露所含数据的布局或者内部结构;它所提供的仅是一些方法而已;

接口类型

一个接口类型定义了一套方法,如果一个具体类型要实现该接口,那么必须实现接口类型定义中的所有方法;


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




本博客所有文章采用的授权方式为 自由转载-非商用-非衍生-保持署名 ,转载请务必注明出处,谢谢。
声明:
本博客欢迎转发,但请保留原作者信息!
博客地址:邱文奇(qiuwenqi)的博客;
内容系本人学习、研究和总结,如有雷同,实属荣幸!
阅读次数:

文章评论

comments powered by Disqus


章节列表