欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解如何利用Golang泛型提高編碼效率

 更新時(shí)間:2023年04月02日 09:59:57   作者:jxwu  
Golang的泛型已經(jīng)出來(lái)有一段時(shí)間了,大家應(yīng)該或多或少對(duì)它有所了解。雖然Golang的泛型在功能上確實(shí)比較簡(jiǎn)單,而且確實(shí)可能會(huì)增加代碼的復(fù)雜度,過(guò)度使用可能還會(huì)降低代碼可讀性。本文就來(lái)介紹一下Golang泛型的相關(guān)知識(shí)吧

前言

Golang的泛型已經(jīng)出來(lái)有一段時(shí)間了,大家應(yīng)該或多或少對(duì)它有所了解,甚至已經(jīng)在應(yīng)用中使用它。雖然Golang的泛型在功能上確實(shí)比較簡(jiǎn)單,而且確實(shí)可能會(huì)增加代碼的復(fù)雜度,過(guò)度使用可能還會(huì)降低代碼可讀性。

但不可否認(rèn)泛型確實(shí)讓我們?cè)谑褂肎olang的時(shí)候能夠抽取一些通用的代碼,避免代碼的重復(fù)拷貝,提高代碼性能(避免類(lèi)型轉(zhuǎn)換),提高編碼的效率和體驗(yàn),提高代碼可維護(hù)性。

這篇文章主要是介紹我使用Golang泛型做過(guò)的事情。

工具函數(shù)

雖然標(biāo)準(zhǔn)庫(kù)里面已經(jīng)提供了大量的工具函數(shù),但是這些工具函數(shù)都沒(méi)有使用泛型實(shí)現(xiàn),為了提高使用體驗(yàn),我們可以使用泛型進(jìn)行實(shí)現(xiàn)。

比如數(shù)值算法里很經(jīng)典的math.Max()、math.Min()都是float64類(lèi)型的,但是很多時(shí)候我們使用的是int、int64這些類(lèi)型,在Golang引入泛型之前,我們經(jīng)常像下面這樣根據(jù)類(lèi)型實(shí)現(xiàn),產(chǎn)生大量模板代碼:

func MaxInt(a, b int) int {
	if a > b {
		return a
	}
	return b
}

func MaxInt64(a, b int64) int64 {
	if a > b {
		return a
	}
	return b
}

// ...其他類(lèi)型

而使用泛型則我們只需要一個(gè)實(shí)現(xiàn):

func Max[T constraints.Ordered](a, b T) T {
	if a > b {
		return a
	}
	return b
}

其中constraints.Ordered表示可排序類(lèi)型,也就是可以使用三路運(yùn)算符的類(lèi)型[>, =, <],包含了所有數(shù)值類(lèi)型和string??梢酝ㄟ^(guò)go get golang.org/x/exp引入。

代碼地址

其他的像json解析、參數(shù)校驗(yàn)、slices等也可以通過(guò)泛型進(jìn)行實(shí)現(xiàn)。

數(shù)據(jù)結(jié)構(gòu)

Golang自帶的泛型容器有slices和map,這兩個(gè)數(shù)據(jù)結(jié)構(gòu)其實(shí)可以完成大部分工作了,但是有時(shí)候我們可能還需要其他的數(shù)據(jù)結(jié)構(gòu),比如說(shuō)優(yōu)先級(jí)隊(duì)列、鏈表等。

雖然Golang在container包下有heaplistring三個(gè)數(shù)據(jù)結(jié)構(gòu),但說(shuō)實(shí)話(huà)使用起來(lái)不是很方便,特別是元素類(lèi)型全是interface{},使用這些結(jié)構(gòu)就需要各種類(lèi)型轉(zhuǎn)換。因此我們可以簡(jiǎn)單的拷貝這些代碼,然后使用泛型進(jìn)行改造,比如heap:

我們不但使用泛型進(jìn)行實(shí)現(xiàn),還把heap默認(rèn)改為使用slice是實(shí)現(xiàn),這樣只需要實(shí)現(xiàn)一個(gè)LessFunc,而不是5個(gè)。

package heap

type LessFunc[T any] func(e1 T, e2 T) bool

type Heap[T any] struct {
	h        []T
	lessFunc LessFunc[T]
}

func New[T any](h []T, lessFunc LessFunc[T]) *Heap[T] {
	heap := &Heap[T]{
		h:        h,
		lessFunc: lessFunc,
	}
	heap.init()
	return heap
}

// 移除堆頂元素
func (h *Heap[T]) Pop() T {
	n := h.Len() - 1
	h.swap(0, n)
	h.down(0, n)
	return h.pop()
}

// 獲取堆頂元素
func (h *Heap[T]) Peek() T {
	return h.h[0]
}

// 添加元素到堆
func (h *Heap[T]) Push(x T) {
	h.push(x)
	h.up(h.Len() - 1)
}

代碼地址

其他的數(shù)據(jù)結(jié)構(gòu)還包括list、setpqueue等。

模板代碼

在后臺(tái)業(yè)務(wù)代碼里面,我們經(jīng)常會(huì)有很多個(gè)業(yè)務(wù)處理函數(shù),每個(gè)業(yè)務(wù)處理函數(shù)我們基本都會(huì)通過(guò)一些代碼封裝成一個(gè)HTTP接口,這里其實(shí)基本上都是模板代碼,比如說(shuō)對(duì)于一個(gè)使用gin實(shí)現(xiàn)的HTTP服務(wù),每個(gè)接口我們都需要進(jìn)行以下處理:

  • 指定HTTP方法、URL
  • 鑒權(quán)
  • 參數(shù)綁定
  • 處理請(qǐng)求
  • 處理響應(yīng)

可以發(fā)現(xiàn),參數(shù)綁定、處理響應(yīng)幾乎都是一樣模板代碼,鑒權(quán)也基本上是模板代碼(當(dāng)然有些鑒權(quán)可能比較復(fù)雜)。

因此我們可以編寫(xiě)一個(gè)泛型模板,把相同的部分抽取出來(lái),用戶(hù)只需要實(shí)現(xiàn)不同接口有差異的指定HTTP方法、URL和處理請(qǐng)求邏輯即可:

// 處理請(qǐng)求
func do[Req any, Rsp any, Opt any](reqFunc ReqFunc[Req],
	serviceFunc ServiceFunc[Req, Rsp], serviceOptFunc ServiceOptFunc[Req, Rsp, Opt], opts ...Opt) gin.HandlerFunc {
	return func(c *gin.Context) {
		// 參數(shù)綁定
		req, err := BindJSON[Req](c)
		if err != nil {
			return
		}
		// 進(jìn)一步處理請(qǐng)求結(jié)構(gòu)體
		if reqFunc != nil {
			reqFunc(c, req)
		}
		var rsp *Rsp
		// 業(yè)務(wù)邏輯函數(shù)調(diào)用
		if serviceFunc != nil {
			rsp, err = serviceFunc(c, req)
		} else if serviceOptFunc != nil {
			rsp, err = serviceOptFunc(c, req, opts...)
		} else {
			panic("must set ServiceFunc or ServiceFuncOpt")
		}
		// 處理響應(yīng)
		ProcessRsp(c, rsp, err)
	}
}

這樣,現(xiàn)在一個(gè)接口基本上只需要一行代碼即可實(shí)現(xiàn)(不包括具體業(yè)務(wù)邏輯函數(shù)):

	// 簡(jiǎn)單請(qǐng)求,不需要認(rèn)證
	e.GET("/user/info/get", ginrest.Do(nil, GetUserInfo))
	// 認(rèn)證,綁定UID,處理
        reqFunc := func(c *gin.Context, req *UpdateUserInfoReq) {
		req.UID = GetUID(c)
	} // 這里拆多一步是為了顯示第一個(gè)參數(shù)是ReqFunc
	e.POST("/user/info/update", Verify, ginrest.Do(reqFunc, UpdateUserInfo))

代碼地址,實(shí)現(xiàn)了一個(gè)基于gin的RESTful風(fēng)格模板。

對(duì)象池/緩存

Golang標(biāo)準(zhǔn)庫(kù)自帶了一個(gè)線(xiàn)程安全、高性能、還能夠根據(jù)對(duì)象熱度自動(dòng)進(jìn)行釋放的對(duì)象池sync.Pool,然而作為對(duì)象池,我們一般只會(huì)往里面放一種類(lèi)型的對(duì)象,但sync.Pool里面的元素還是interface{}類(lèi)型,因此我們可以簡(jiǎn)單的封裝sync.Pool,讓它里面的元素有具體類(lèi)型:

這里其實(shí)就是簡(jiǎn)單的對(duì)象sync.Pool進(jìn)行包裝,然后添加了一個(gè)ClearFunc()在回收對(duì)象的時(shí)候進(jìn)行一些清理操作,比如說(shuō)byte切片我們需要讓它的已用長(zhǎng)度歸零(容量還是不變)。

// 創(chuàng)建新對(duì)象
type NewFunc[T any] func() T

// 清理對(duì)象
type ClearFunc[T any] func(T) T

type Pool[T any] struct {
	p         sync.Pool
	clearFunc ClearFunc[T]
}

func New[T any](newFunc NewFunc[T], clearFunc ClearFunc[T]) *Pool[T] {
	if newFunc == nil {
		panic("must be provide NewFunc")
	}
	p := &Pool[T]{
		clearFunc: clearFunc,
	}
	p.p.New = func() any {
		return newFunc()
	}
	return p
}

// 獲取對(duì)象
func (p *Pool[T]) Get() T {
	return p.p.Get().(T)
}

// 歸還對(duì)象
func (p *Pool[T]) Put(t T) {
	if p.clearFunc != nil {
		t = p.clearFunc(t)
	}
	p.p.Put(t)
}

作為字節(jié)數(shù)組對(duì)象池使用:

	newFunc := func() []byte {
		return make([]byte, size, cap)
	}
	clearFunc := func(b []byte) []byte {
		return b[:0]
	}
	p := New(newFunc, clearFunc)
	bytes := p.Get() // 這里bytes類(lèi)型是[]byte
	p.Put(bytes)

代碼地址

對(duì)于緩存也是同理,目前大部分緩存庫(kù)的實(shí)現(xiàn)都是基于interface{}或者是byte[],但是我們還是更加喜歡直接操作具體類(lèi)型,因此我們可以自己使用泛型實(shí)現(xiàn)(或改造)一個(gè)緩存庫(kù)。我自己也實(shí)現(xiàn)了一個(gè)泛型緩存策略庫(kù),里面包含LRU、LFU、ARC、NearlyLRU、TinyLFU等緩存策略。

總結(jié)

可以看到,其實(shí)Golang泛型主要提供了一種代碼抽象、封裝的能力,讓我們能夠?qū)懗龈軓?fù)用的代碼,避免代碼到處拷貝,從而能夠提高代碼的可維護(hù)性,可讀性,還能從避免類(lèi)型轉(zhuǎn)換中得到一點(diǎn)性能提升。

到此這篇關(guān)于詳解如何利用Golang泛型提高編碼效率的文章就介紹到這了,更多相關(guān)Golang泛型內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • golang json數(shù)組拼接的實(shí)例

    golang json數(shù)組拼接的實(shí)例

    這篇文章主要介紹了golang json數(shù)組拼接的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-04-04
  • Go語(yǔ)言中的指針運(yùn)算實(shí)例分析

    Go語(yǔ)言中的指針運(yùn)算實(shí)例分析

    這篇文章主要介紹了Go語(yǔ)言中的指針運(yùn)算技巧,實(shí)例分析了Go語(yǔ)言指針運(yùn)算的實(shí)現(xiàn)方法,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-02-02
  • Go語(yǔ)言的互斥鎖的詳細(xì)使用

    Go語(yǔ)言的互斥鎖的詳細(xì)使用

    本文主要介紹了Go語(yǔ)言的互斥鎖的詳細(xì)使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • 使用go實(shí)現(xiàn)刪除sql里面的注釋和字符串功能(demo)

    使用go實(shí)現(xiàn)刪除sql里面的注釋和字符串功能(demo)

    這篇文章主要介紹了使用go實(shí)現(xiàn)刪除sql里面的注釋和字符串功能,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-11-11
  • Golang搭建grpc環(huán)境的流程步驟

    Golang搭建grpc環(huán)境的流程步驟

    這篇文章主要給大家介紹了Golang搭建grpc環(huán)境的流程步驟,文中通過(guò)圖文結(jié)合的方式給大家講解的非常詳細(xì),對(duì)大家了解Golang搭建grpc環(huán)境有一定的幫助,需要的朋友可以參考下
    2024-03-03
  • grpcurl通過(guò)命令行訪(fǎng)問(wèn)gRPC服務(wù)

    grpcurl通過(guò)命令行訪(fǎng)問(wèn)gRPC服務(wù)

    這篇文章主要為大家介紹了grpcurl通過(guò)命令行訪(fǎng)問(wèn)gRPC服務(wù)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • Go語(yǔ)言流程控制之goto語(yǔ)句與無(wú)限循環(huán)

    Go語(yǔ)言流程控制之goto語(yǔ)句與無(wú)限循環(huán)

    這篇文章主要介紹了Go語(yǔ)言流程控制之goto語(yǔ)句與無(wú)限循環(huán),是golang入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下
    2015-10-10
  • golang實(shí)現(xiàn)簡(jiǎn)易的分布式系統(tǒng)方法

    golang實(shí)現(xiàn)簡(jiǎn)易的分布式系統(tǒng)方法

    這篇文章主要介紹了golang實(shí)現(xiàn)簡(jiǎn)易的分布式系統(tǒng)方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-10-10
  • golang基于errgroup實(shí)現(xiàn)并發(fā)調(diào)用的方法

    golang基于errgroup實(shí)現(xiàn)并發(fā)調(diào)用的方法

    這篇文章主要介紹了golang基于errgroup實(shí)現(xiàn)并發(fā)調(diào)用,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-09-09
  • go編程中g(shù)o-sql-driver的離奇bug解決記錄分析

    go編程中g(shù)o-sql-driver的離奇bug解決記錄分析

    這篇文章主要為大家介紹了go編程中g(shù)o-sql-driver的離奇bug解決記錄分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-05-05

最新評(píng)論