IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发测试 -> Go的接口(interface)介绍和使用 -> 正文阅读

[开发测试]Go的接口(interface)介绍和使用

1. 接口(interface)的类型

在Go语言中接口(interface)是一种类型,是一种抽象的类型。

首先我们定义interface类型变量

func main(){
	// 定义一个空接口x
	var x interface{}
    fmt.Printf("type:%T value:%v\n", x, x)

	s := "hello world!"
	x = s
	fmt.Printf("type:%T value:%v\n", x, x)

	i := 128
	x = i
	fmt.Printf("type:%T value:%v\n", x, x)

	member := Member{}
	x = member
	fmt.Printf("type:%T value:%v\n", x, x)

    // 打印结果
    // type:<nil> value:<nil>
    // type:string value:hello world!
    // type:int value:128
    // type:main.Member value:{ 0}

    // 空类型作为map的value值定义类型
    var studentInfo = make(map[string]interface{})
}

// 空接口作为函数参数
func show(a interface{}) {
    fmt.Printf("type:%T value:%v\n", a, a)
}

从上面的例子中我们可以知道:

1.定义空的interface的它的类型和值都为nil, 那么就可以通过!=nil做interface的判断

2.空接口类型的变量可以存储任意类型的变量。空接口是指没有定义任何方法的接口,因此任何类型都实现了空接口。

3.空接口常用的可以作为map的值定义类型和用户函数参数

2.接口(interface)的定义和使用

和面向对象接口类型相似,接口(interface)是为了不暴露出它所代表的对象的内部值的结构和这个对象支持的基础操作的集合;它们只会展示对外提供操作自己的方法。

  • 接口(interface)的定义
    ???????

    Go源码io包下的Reader和Writer接口定义:

  • // Reader is the interface that wraps the basic Read method.
    type Reader interface {
    	Read(p []byte) (n int, err error)
    }
    
    // Writer is the interface that wraps the basic Write method.
    type Writer interface {
    	Write(p []byte) (n int, err error)
    }
    
    // Closer is the interface that wraps the basic Close method.
    type Closer interface {
    	Close() error
    }

    io.Writer类型是用的最广泛的接口之一,因为它提供了所有的类型写入bytes的抽象,包括文件类型,内存缓冲区,网络链接,HTTP客户端,压缩工具,哈希等等

    除了直接定义接口方法,Go的接口还能嵌套

    // ReadWriter is the interface that groups the basic Read and Write methods.
    type ReadWriter interface {
    	Reader
    	Writer
    }
    
    // ReadCloser is the interface that groups the basic Read and Close methods.
    type ReadCloser interface {
    	Reader
    	Closer
    }
    
    // WriteCloser is the interface that groups the basic Write and Close methods.
    type WriteCloser interface {
    	Writer
    	Closer
    }
    
    // ReadWriteCloser is the interface that groups the basic Read, Write and Close methods.
    type ReadWriteCloser interface {
    	Reader
    	Writer
    	Closer
    }

    接口的定义就是使用interface定义一组方法。这里一般方法名大写,表示其他的包可以使用该接口方法

  • 接口(interface)的实现

    Go的接口(interface)实现是一种称为鸭子模型的实现。接口(interface)就像是定义一个规则集合,只要实现了接口(interface)的提供的所有方法,那么就表示该实现了该接口,不用像java那种在类上显示的去指定实现的接口。

    我们看下bytes.Buffer实现的Reader和Writer接口

    package bytes
    
    // A Buffer is a variable-sized buffer of bytes with Read and Write methods.
    type Buffer struct {
    	buf      []byte // contents are the bytes buf[off : len(buf)]
    	off      int    // read at &buf[off], write at &buf[len(buf)]
    	lastRead readOp // last read operation, so that Unread* can work correctly.
    }
    
    
    // 实现Write方法
    func (b *Buffer) Write(p []byte) (n int, err error) {
    	b.lastRead = opInvalid
    	m, ok := b.tryGrowByReslice(len(p))
    	if !ok {
    		m = b.grow(len(p))
    	}
    	return copy(b.buf[m:], p), nil
    }
    
    // 实现Read方法
    func (b *Buffer) Read(p []byte) (n int, err error) {
    	b.lastRead = opInvalid
    	if b.empty() {
    		// Buffer is empty, reset to recover space.
    		b.Reset()
    		if len(p) == 0 {
    			return 0, nil
    		}
    		return 0, io.EOF
    	}
    	n = copy(p, b.buf[b.off:])
    	b.off += n
    	if n > 0 {
    		b.lastRead = opRead
    	}
    	return n, nil
    }

    这里看到Buffer实现了Reader的Read和Write方法,而判断是否实现了某个接口(interface)的是要保证实现的所有方法的签名必须一致。签名一致要求的是实现的接口的方法必须保证名称相同、参数列表相同以及返回值相同。 这里我们可以看到Buffer实现的Read和Write方法和定义的Reader和Writer的一致。

  • 接口(interface)的断言和类型选择? ? ? ??

    第1部分里面我们看到空的interface的可以赋值给任意类型,那么我们怎么判断实际有没有实现某个接口呢?这里go提供一种断言的方式来判断,语法格式如下:

    value, ok := x.(T)
    
    // x:表示类型为interface{}的变量
    // T:表示断言x可能是的类型。
    // value :成功转换后的值,动态类型
    // ok:是否为目标类型,bool 类型
  • 这里需要注意的是,如果x == nil, 那么断言一定失败。如果在已经确定?x?就是?T?类型的前提下,也可以省略?ok,但是只要类型不匹配,就会报 panic。

  • 我们来看看了bytes.Buffer实现了Reader和Writer的接口,下面用断言来判断一下

    var r io.Reader
    r = bytes.NewBufferString("111")
    rw1, ok := r.(io.ReadWriter)
    fmt.Printf("result: ok->%v, value->%v\n", ok,rw1)
    
    var w io.Writer
    w = bytes.NewBufferString("222")
    rw2, ok := w.(io.ReadWriter)
    fmt.Printf("result: ok->%v, value->%v\n", ok,rw2)
    
    var stdw io.Writer = os.Stdout
    f, ok := stdw.(*os.File)
    fmt.Printf("result: ok->%v, value->%v\n",ok,f.Name())
    
    // 打印结果
    // result: ok->true, value->111
    // result: ok->true, value->222
    // result: ok->true, value->/dev/stdout
    

    如果判断一个空接口类型有很多类型需要判断的时候,我们可以使用go的类型分支结构来处理,结构如下:

    switch x.(type) {
        case nil:       
             // todo...
        case int, uint: 
             // todo...
        case bool:      
             // todo...
        case string:    
             // todo...
        default:        
             // todo...
    }
    ?????????

3.接口(interface)在go-kratos框架中的使用

// 判断config是否实现了Config接口
_ Config = (*config)(nil)

// Config is a config interface.
type Config interface {
	Load() error
	Value(key string) Value
	Watch(key string, o Observer) error
	Close() error
}

// 定义具体实现
type config struct {
	opts      options
	reader    Reader
	cached    sync.Map
	observers sync.Map
	watchers  []Watcher
	log       *log.Helper
}

// 通过options创建Config实例
func New(opts ...Option) Config {
	return &config{
		opts:   o,
		reader: newReader(o),
		log:    log.NewHelper(o.logger),
	}
}

func (c *config) Load() error {
    // todo: 具体加载配置方法实现
	return nil
}

func (c *config) Value(key string) Value {
    // todo: 取配置某个key对应的值
	return nil
}


func (c *config) Watch(key string, o Observer) error {
	// todo: 监听配置值变化
	return nil
}

func (c *config) Close() error {
	//  todo: 关闭配置文件
	return nil
}

说明:这里这么实现的几点意图

1. 定义config为小写,外部包不能访问,是为了隐藏包内部具体的实现细节。

2. 对外只提供Config的interface来方法操作,实现与内部实现隔离。

3. _ Config = (*config)(nil),这句代码判断config是否实现了Config接口,如果 *Config 与 config的接口不匹配, 那么语句_ Config = (*config)(nil)将无法编译通过。将实例是否实现接口提前到编译期就表现出来

参考资料:

1.kratos的配置包:https://github.com/go-kratos/kratos/tree/main/config

2.接口 · Go语言中文文档

3. uber手册interface的合法校验:GitHub - keenw/uber_go_guide_cn: Uber Go 语言编码规范中文版. The Uber Go Style Guide .

  开发测试 最新文章
pytest系列——allure之生成测试报告(Wind
某大厂软件测试岗一面笔试题+二面问答题面试
iperf 学习笔记
关于Python中使用selenium八大定位方法
【软件测试】为什么提升不了?8年测试总结再
软件测试复习
PHP笔记-Smarty模板引擎的使用
C++Test使用入门
【Java】单元测试
Net core 3.x 获取客户端地址
上一篇文章      下一篇文章      查看所有文章
加:2022-03-24 00:53:03  更:2022-03-24 00:53:19 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/18 0:28:38-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码