go语言模式原型 go 语言原本

如何看待go语言泛型的最新设计?

Go 由于不支持泛型而臭名昭著,但最近,泛型已接近成为现实。Go 团队实施了一个看起来比较稳定的设计草案,并且正以源到源翻译器原型的形式获得关注。本文讲述的是泛型的最新设计,以及如何自己尝试泛型。

为新城等地区用户提供了全套网页设计制作服务,及新城网站建设行业解决方案。主营业务为成都网站设计、网站制作、新城网站设计,以传统方式定制建设网站,并提供域名空间备案等一条龙服务,秉承以专业、用心的态度为用户提供真诚的服务。我们深信只要达到每一位用户的要求,就会得到认可,从而选择与我们长期合作。这样,我们也可以走得更远!

例子

FIFO Stack

假设你要创建一个先进先出堆栈。没有泛型,你可能会这样实现:

type Stack []interface{}func (s Stack) Peek() interface{} {

return s[len(s)-1]

}

func (s *Stack) Pop() {

*s = (*s)[:

len(*s)-1]

}

func (s *Stack) Push(value interface{}) {

*s = 

append(*s, value)

}

但是,这里存在一个问题:每当你 Peek 项时,都必须使用类型断言将其从 interface{} 转换为你需要的类型。如果你的堆栈是 *MyObject 的堆栈,则意味着很多 s.Peek().(*MyObject)这样的代码。这不仅让人眼花缭乱,而且还可能引发错误。比如忘记 * 怎么办?或者如果您输入错误的类型怎么办?s.Push(MyObject{})` 可以顺利编译,而且你可能不会发现到自己的错误,直到它影响到你的整个服务为止。

通常,使用 interface{} 是相对危险的。使用更多受限制的类型总是更安全,因为可以在编译时而不是运行时发现问题。

泛型通过允许类型具有类型参数来解决此问题:

type Stack(type T) []Tfunc (s Stack(T)) Peek() T {

return s[len(s)-1]

}

func (s *Stack(T)) Pop() {

*s = (*s)[:

len(*s)-1]

}

func (s *Stack(T)) Push(value T) {

*s = 

append(*s, value)

}

这会向 Stack 添加一个类型参数,从而完全不需要 interface{}。现在,当你使用 Peek() 时,返回的值已经是原始类型,并且没有机会返回错误的值类型。这种方式更安全,更容易使用。(译注:就是看起来更丑陋,^-^)

此外,泛型代码通常更易于编译器优化,从而获得更好的性能(以二进制大小为代价)。如果我们对上面的非泛型代码和泛型代码进行基准测试,我们可以看到区别:

type MyObject struct {

int

}

var sink MyObjectfunc BenchmarkGo1(b *testing.B) {

for i := 0; i  b.N; i++ {

var s Stack

s.Push(MyObject{})

s.Push(MyObject{})

s.Pop()

sink = s.Peek().(MyObject)

}

}

func BenchmarkGo2(b *testing.B) {

for i := 0; i  b.N; i++ {

var s Stack(MyObject)

s.Push(MyObject{})

s.Push(MyObject{})

s.Pop()

sink = s.Peek()

}

}

结果:

BenchmarkGo1BenchmarkGo1-16     12837528         87.0 ns/op       48 B/op        2 allocs/opBenchmarkGo2BenchmarkGo2-16     28406479         41.9 ns/op       24 B/op        2 allocs/op

在这种情况下,我们分配更少的内存,同时泛型的速度是非泛型的两倍。

合约(Contracts)

上面的堆栈示例适用于任何类型。但是,在许多情况下,你需要编写仅适用于具有某些特征的类型的代码。例如,你可能希望堆栈要求类型实现 String() 函数

Go语言实践模式 - 函数选项模式(Functional Options Pattern)

大家好,我是小白,有点黑的那个白。

最近遇到一个问题,因为业务需求,需要对接第三方平台.

而三方平台提供的一些HTTP(S)接口都有统一的密钥生成规则要求.

为此我们封装了一个独立的包 xxx-go-sdk 以便维护和对接使用.

其中核心的部分是自定义HTTP Client,如下:

一些平台会要求appKey/appSecret等信息,所以Client结构体就变成了这样,这时参数还比较少, 而且是必填的参数,我们可以提供构造函数来明确指定。

看起来很满足,但是当我们需要增加一个 Timeout 参数来控制超时呢?

或许你会说这还不简单,像下面一样再加一个参数呗

那再加些其他的参数呢?那构造函数的参数是不是又长又串,而且每个参数不一定是必须的,有些参数我们又会考虑默认值的问题。

为此,勤劳但尚未致富的 gophers 们使用了总结一种实践模式

首先提取所有需要的参数到一个独立的结构体 Options,当然你也可以用 Configs 啥的.

然后为每个参数提供设置函数

这样我们就为每个参数设置了独立的设置函数。返回值 func(*Options) 看着有点不友好,我们提取下定义为单个 Option 调整一下代码

当我们需要添加更多的参数时,只需要在 Options 添加新的参数并添加新参数的设置函数即可。

比如现在要添加新的参数 Timeout

这样后续不管新增多少参数,只需要新增配置项并添加独立的设置函数即可轻松扩展,并且不会影响原有函数的参数顺序和个数位置等。

至此,每个选项是区分开来了,那么怎么作用到我们的 Client 结构体上呢?

首先,配置选项都被提取到了 Options 结构体重,所以我们需要调整一下 Client 结构体的参数

其次,每一个选项函数返回 Option,那么任意多个就是 ...Option,我们调整一下构造函数 NewClient 的参数形式,改为可变参数,不再局限于固定顺序的几个参数。

然后循环遍历每个选项函数,来生成Client结构体的完整配置选项。

那么怎么调用呢?对于调用方而已,直接在调用构造函数NewClient()的参数内添加自己需要的设置函数(WithXXX)即可

当需要设置超时参数,直接添加 WithTimeout即可,比如设置3秒的超时

配置选项的位置可以任意设置,不需要受常规的固定参数顺序约束。

可以看到,这种实践模式主要作用于配置选项,利用函数支持的特性来实现的,为此得名 Functional Options Pattern,优美的中国话叫做「函数选项模式」。

最后, 我们总结回顾一下在Go语言中函数选项模式的优缺点

go语言os怎么创建文件后格式化显示

再简单不过了,给一个路径给它,返回文件描述符,如果出现错误就会返回一个 *PathError。

这是一个只读打开模式,实际上就是 os.OpenFile() 的快捷操作,它的原型如下:

go语言的全称

Go全称Golang。

Go语言由Google公司开发,并于2009年开源,相比Java/Python/C等语言,Go尤其擅长并发编程,性能堪比C语言,开发效率肩比Python,被誉为“21世纪的C语言”。

Go语言在云计算、大数据、微服务、高并发领域应用应用非常广泛。BAT大厂正在把Go作为新项目开发的首选语言。


文章题目:go语言模式原型 go 语言原本
URL分享:http://myzitong.com/article/ddggjpi.html