Interface

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

内容简介: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,这就解决了指针传入的问题


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

查看所有标签

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

The Filter Bubble

The Filter Bubble

Eli Pariser / Penguin Press / 2011-5-12 / GBP 16.45

In December 2009, Google began customizing its search results for each user. Instead of giving you the most broadly popular result, Google now tries to predict what you are most likely to click on. Ac......一起来看看 《The Filter Bubble》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

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

正则表达式在线测试