浅谈go中的interface

浅谈go中的interface

  • 什么是interface
  • interface的意义
  • interface的官方示例
  • interface的设计理念
  • 基于接口编程

什么是interface

简单的说,interface是一组method的组合,我们通过interface来定义对象的一组行为。

官方文档 中对 Interface 是这样定义的:

An interface type specifies a method set called its interface. A variable of interface type can store a value of any type with a method set that is any superset of the interface. Such a type is said to implement the interface. The value of an uninitialized variable of interface type is nil.

1An interface type specifies a method set called its interface. A variable of interface type can store a value of any type with a method set that is any superset of the interface. Such a type is said to implement the interface. The value of an uninitialized variable of interface type is nil.

一个 interface 类型定义了一个 方法集 作为其 接口。 interface 类型的变量可以保存含有属于这个 interface 类型方法集超集的任何类型的值,这时我们就说这个类型 实现 了这个 接口。未被初始化的 interface 类型变量的零值为 nil。

对于 interface 类型的方法集来说,其中每一个方法都必须有一个不重复并且不是 补位名(即单下划线 _)的方法名。

interface特性是
- 是一组函数签名的集合
- 是一种类型

interface的意义

Go语言的主要设计者之一罗布·派克(Rob Pike)曾经说过,如果只能选择一个Go语言的特性移植到其他语言中,他会选择接口。接口在Go语言有着至关重要的地位。如果说goroutine和channel 是支撑起Go语言的并发模型的基石,让Go语言在如今集群化与多核化的时代成为一道极为亮丽的风景,那么接口是Go语言整个类型系统的基石,让Go语言在基础编程哲学的探索上达到前所未有的高度。

interface的官方示例

官方文档的例子:

如果我们定义 File 接口类型:

// A simple File interface
type File interface {
    Read(b Buffer) bool
    Write(b Buffer) bool
    Close()
}

那么,假设有两个其他类型 S1 和 S2 都拥有下面的方法集:

func (p T) Read(b Buffer) bool { return … }
func (p T) Write(b Buffer) bool { return … }
func (p T) Close() { … }

注意,上面代码中的 T 代表 S1 或者 S2。这时,无论 S1 和 S2 各自的方法集中还有哪些方法,我们都说 S1 和 S2 各自 实现 了接口 File。

对于空接口 interface {} 来说,由于其不含有任何方法签名,在 Golang 语言中我们可以认为所有类型都 实现 了空接口。

一个类型可以 实现 一个或者多个接口,假设我们还有下面的接口类型:

type Locker interface {
    Lock()
    Unlock()
}

而 S1 和 S2 各自的方法集中都含有下面的方法签名:

func (p T) Lock() { … }
func (p T) Unlock() { … }

同样的,上面代码中的 T 代表 S1 或者 S2,这时我们称 S1 和 S2 各自都 实现 了接口 File 和 Locker。

一个接口类型(假设为 T)可以 嵌入 另一个接口类型(假设为 E),这被称为 embedding interface E in T,而且,所有 E 中的方法都会被添加到 T 中。需要注意的是,一个接口不能 嵌入 其本身,也不能 嵌入 任何已经 嵌入 其本身的其他接口,这样会造成递归 嵌入。
关于 interface 更多需要注意的地方还有:

  • 接口类型不包含任何数据成员。
  • 接口类型进行转换的时候,默认返回的是临时对象,所以如果需要对原始对象进行修改,需要获取原始对象的指针。

interface的设计理念

Go语言中Interface淡化了面向对象中接口应具有的象征意义,接口在Go语言中仅仅只是“表现形式”上相同的一类事物的抽象概念。在Go语言中只要是具有相同“表现形式”的“类型”都具有相同的Interface,而不需要考虑这个Interface在具体的使用中应具有的实际意义。这样的话,就把Interface的成本交给使用者了,而不是交给设计者。

设计者只负责设计method,也就是Interface的构成元素,Interface在具体的使用中应该有的实际意义都是交给Interface的使用者去确定。举个最简单的例子:

跑假设有两种意思:一种是逃跑,英文叫escape;另一种是跑步,英文叫run。如果中文就一种,把他叫做pao。

type BadMan struct{
}
    func (badMan *BadMan) pao(){
}

type SportMan struct{
}
    func (sportMan *SportMan) pao(){
}
//设计人员只考虑这部分
type Pao Interface{
pao()
} 
//这个部分由使用者考虑
var paoA Pao = new(BadMan)
var paoB Pao = new(SportMan)

虽然两个类型的pao接口实际意义不一样,但对Go语言来说,我认为他们的接口是一样的,因为“表现形式”相同。至于在什么情景下应该去使用BadMan或SportMan的pao接口才能正确地完成任务,这个是使用者考虑的,设计Interface的人员不要考虑。

go的interface更加接近interface这个概念的本质,因为对于实现了这个interface的类型里面的任何一个方法的类型而言,都可以划归到这个interface种群里面。而面向对象设计语言里面,类型必须安全实现interface的内容才能被其管理,否则就是一个抽象类。离诞生对象的类型还差一布。

在抽象与实例化的道路上,go语言一步到位,而java和cpp如果想一步到位的化,那么必须实现interface的全部方法。

基于接口编程

C++中不支持接口,接口的实现方式是用纯虚函数来实现的,C#具有接口,但是它认为接口是对象的一个能力,这是一个很大的进步,但是它不大灵活,比如

public Interface IFile
{
    public int Read(string filePath, int len)
    public int Write(string filePath, int len)
}
这个接口实现从一个文件中读取len个字节数据,返回实际的数据,以及写入len个字节数据,返回实际写入的数据个数
public class CDataNode:IFile
{
    public int Read(string filePath, int len){...}
    public int Write(string filePath, int len){...}
}

这样CDataNode就可以在支持IFile接口的地方直接使用

这样的代码写了很久,但是如果发现IFile这个粒度太大了,有些地方只需要Read,有些地方需要Write,在C#中只能是

public Interface IRead
public Interface IWrite
public Interface IFile:IRead,IWrite

在go中不需要这样,只需要再声明IRead和IWrite接口,IFile可以直接转换到IRead和IWrite,它更加灵活。
package main

import (
"fmt"
)

type iFile interface {
    Read(int) int
    Write(int) int
}

type rwFile struct {
}

func (rw rwFile) Read(int) int {
    fmt.Println("rwFile read")
    return 0
}
func (rw rwFile) Write(int) int {
    fmt.Println("rwFile write")
    return 0
}

type iRead interface {
    Read(int) int
}

type iWrite interface {
    Write(int) int
}

func TestInterface() {
    var rwInterface iFile = rwFile{}
    rwInterface.Read(0)
    rwInterface.Write(0)

    var rInterface iRead = rwFile{}
    rInterface.Read(0)

    var wInterface iWrite = rwFile{}
    wInterface.Write(0)
}

模块之间依赖接口,并且go是不支持继承,只支持组合,继承是一种强关系,一旦定下来就很难改动,或者说改动成本非常之高,组合是一种弱关系,更加松散一点。

注:此篇只是简单介绍了interface的基本定义和应用,至于进阶,接口赋值、接口查询、类型查询、接口组合等相关知识,后续文章补充。

参考资料:
- 《Go语言编程》 作者:许式伟等
- 博客go中的interface http://blog.csdn.net/jacob_007/article/details/53557074
- 博客interface理念 https://studygolang.com/articles/2242
- 博客interface基础 http://blog.csdn.net/uudou/article/details/52456133

猜你喜欢

转载自blog.csdn.net/qq_21794823/article/details/78967719