Interface

栏目: Go · 发布时间: 5年前

内容简介:golang是面向接口的编程语言,这是golang的语言特点之一。golang传统意义上的继承,多态等,它只支持封装。golang中的接口并不是传统(c/c++,java等)意义上的接口,而是传统面向对象中继承和多态在golang中是通过接口来完成的。在golang的interface出现之前,接口主要作为不同组建之间的契约存在的,对契约的实现是强制的,必须明确声明实现了这个接口。但是golang完全不同。多态是指代码可以根据类型的具体实现采用不同行为的能力,接口是一个或多个方法签名的集合,只要某个类型拥

接口interface

golang是面向接口的编程语言,这是golang的语言特点之一。golang传统意义上的继承,多态等,它只支持封装。golang中的接口并不是传统(c/c++,java等)意义上的接口,而是传统面向对象中继承和多态在golang中是通过接口来完成的。在golang的interface出现之前,接口主要作为不同组建之间的契约存在的,对契约的实现是强制的,必须明确声明实现了这个接口。但是golang完全不同。多态是指代码可以根据类型的具体实现采用不同行为的能力,接口是一个或多个方法签名的集合,只要某个类型拥有该接口的所有方法签名,即算实现该接口,无需显示声明实现了哪个接口,这称为StructuralTyping,接口只有方法声明,没有实现,没有数据字段接口可以匿名嵌入其它接口,或嵌入到结构中,将对象赋值给接口时,会发生拷贝,而接口内部存储的是指向这个复制品的指针,既无法修改复制品的状态,也无法获取指针,只有当接口存储的类型和对象都为nil时,接口才等于nil,接口调用不会做receiver的自动转换,接口同样支持匿名字段方法,接口也可实现类似OOP中的多态,空接口可以作为任何类型数据的容器

eg:

type Traversal interface{
    Traverse()
}
func main(){
    traversal := getTraversal()
    traversal.Traverse()
}

通过getTraversal()方法封装得到了一个traversal,这个traversal实现了Traversal这个interface

duck typing

许多语言支持duck typing,包括c++,python, java无法实现duck typing

duck typing的概念:像鸭子走路,像鸭子叫,长的像鸭子,那么它就是一只鸭子

duck typing的用途:描述事物的外部行为而非内部结构

在golang编程中就利用duck typing的方式进行,gopher不用在意调用方法的“类型”是一个什么,当gopher需要调用到某个方法时能提供这个方法即可,然后和这些方法进行互动

长得像鸭子就是鸭子,也就是说实现了interface就是实现了interface中定义的那个方法,那么实现该方法的struct就不用赋值给interface就拥有了这个能力。也就是说实现了这个struct的实例可以被fmt.Println调用。但是在定义的时候其实是interface实例被调用。只不过这个struct实现了这个接口,所以它也拥有了这个能力。

严格说来 go 属于结构化类型系统,类似duck typing,因为在定义duck typing时强调了“动态绑定”,但是golang是编译时就绑定了,从这个角度看,golang就不是duck typing

golang接口的定义

graph LR
使用者-->实现者

golang中,一个类只需要实现接口要求的所有函数,我们就说这个类实现了这个接口

golang的非侵入式接口,看似做了很小的语法调整,实则影响很是深远:

  1. go标准库,再也不需要绘制类库继承树图;
  2. 实现类的时候,只需要关心自己应该实现哪些方法,不必纠结接口要拆得多细才合理,接口由使用方法按需定义,而不用事前规划;
  3. 不用为了实现一个接口而导入一个包,因为多引用一个外部包,就意味着更多的耦合,接口使用方按自身需求定义,使用方无需关心是否有其他模块定义过类似的接口。

代码示例,自主实现一个下载https://studygolang.com/网页的代码(其中含有多态的概念):

main()

package main

import (
	"fmt"
	"interfaceType/mock"
	real2 "interfaceType/real"
)

type Link interface {
	Get(url string) string
}

func download(l Link)string  {
	return l.Get("https://studygolang.com/")
}
func main() {
	var l,m Link
	l = mock.Retriever{"This is a fack link"}
	fmt.Println(download(l))
	m = new(mock.Retriever)
	fmt.Println(download(m))

	var r Link
	r = real2.Retriever{}
	fmt.Println(download(r))
}

mockRetriever

package mock

type Retriever struct {
	Contents string
}

func (r Retriever) Get(url string) string {
	if r.Contents == ""{
		return url
	}
	return r.Contents
}

realRetriever

package real

import (
	"time"
	"net/http"
	"net/http/httputil"
)

type Retriever struct {
	UserAgent string
	TimeOut   time.Duration
}

func (r Retriever) Get(url string) string {
	response, err := http.Get(url)
	if err != nil {
		panic(err)
	}
	result, err := httputil.DumpResponse(response, true)
	if err != nil {
		panic(err)
	}
	defer response.Body.Close()
	return string(result)

}

利用标准库实现下载一个下载https://studygolang.com/网页的代码:

package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

// main is the entry point for the application.
func main() {
	// Get a response from the web server.
	r, err := http.Get("https://studygolang.com/")
	if err != nil {
		fmt.Println(err)
		return
	}
	// Copies from the Body to Stdout.
	io.Copy(os.Stdout, r.Body)
	if err := r.Body.Close(); err != nil {
		fmt.Println(err)
	}
}

观察以上代码,不难发现,使用标准库实现网页的download更容易一些当然,我们这里要研究的东西是golang的接口,download的实现是次要的。

r, err := http.Get("https://studygolang.com/")

利用http.Get()函数得到一个http.Response类型的指针,观察一下http.Get()函数源码:

func Get(url string) (resp *Response, err error) {
	return DefaultClient.Get(url)
}

在观察一下http.Response:

type Response struct {
	Status     string 
	StatusCode int   
	Proto      string 
	ProtoMajor int   
	ProtoMinor int   
	Header Header
	Body io.ReadCloser
	ContentLength int64
	TransferEncoding []string
	Close bool
	Uncompressed bool
	Trailer Header
	Request *Request
	TLS *tls.ConnectionState
}

原来http.Response是一个结构,不难发现里面的Body是一个io.ReadCloser类型的,让我们再去看看io.ReadCloser的源码:

type ReadCloser interface {
	Reader
	Closer
}

原来Body是一个接口组合,实现了Reader和Closer方法

接下来继续看

io.Copy(os.Stdout, r.Body)

io.Copy()函数的源码:

func Copy(dst Writer, src Reader) (written int64, err error) {
	return copyBuffer(dst, src, nil)
}

第一个参数是一个Writer,第二个参数是一个Reader,当然,我们的Body实现了Reader这个接口,所以可以直接作为参数传入。

接口赋值

golang接口的赋值有以下两种:

  1. 将对象实例赋值给接口:要求该对象实例实现了接口要求的所有方法
  2. 将一个接口赋值给另外一个接口:

将实例化对象赋值给接口

package main

import "fmt"

type myInt int
type LessAdd interface {
	Less(b myInt) bool
	Add(b myInt)
}

func main() {
	var a myInt = 2
	var b LessAdd = &a
	b.Add(a)
	fmt.Println(b.Less(0))
	fmt.Printf("%d\n",a)
}
func (a myInt) Less(b myInt) bool {
	return a < b
}
func (a *myInt) Add(b myInt) {
	*a += b
}

有一个有趣的问题:a赋值给接口LessAdd时候,用的是地址的引用,而不是值的引用

var a myInt = 2
	var b LessAdd = &a

接口LessAdd中包含了一个指针的方法func (a *myInt) Add(b myInt),在调用非指针方法func (a myInt) Less(b myInt) bool时候,golang会自动生成一个函数func(a *myInt)Less(b myInt)bool,这就解决了指针传入的问题


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

决战大数据

决战大数据

车品觉 / 浙江人民出版社 / 2014-3-1 / 45.9

[内容简介]  大数据时代的来临,给当今的商业带来了极大的冲击,多数电商人无不“谈大数据色变”,并呈现出一种观望、迷茫、手足无措的状态。车品觉,作为一名经验丰富的电商人,在敬畏大数据的同时,洞悉到了数据时代商业发展的更多契机,他创新了数据框架的建立和使用,重新量化了数据价值的指标,并挖掘了在无线数据和多屏时代下商业发展的本质……在他看来,改变思维方式,即可改变数据和商业的未来。  ......一起来看看 《决战大数据》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

SHA 加密
SHA 加密

SHA 加密工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试