最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

前言

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

但不可否認泛型確實讓我們在使用Golang的時候能夠抽取一些通用的代碼,避免代碼的重復拷貝,提高代碼性能(避免類型轉換),提高編碼的效率和體驗,提高代碼可維護性。

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

工具函數(shù)

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

比如數(shù)值算法里很經(jīng)典的math.Max()、math.Min()都是float64類型的,但是很多時候我們使用的是int、int64這些類型,在Golang引入泛型之前,我們經(jīng)常像下面這樣根據(jù)類型實現(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
}

// ...其他類型

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

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

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

代碼地址

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

數(shù)據(jù)結構

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

雖然Golang在container包下有heap、listring三個數(shù)據(jù)結構,但說實話使用起來不是很方便,特別是元素類型全是interface{},使用這些結構就需要各種類型轉換。因此我們可以簡單的拷貝這些代碼,然后使用泛型進行改造,比如heap:

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

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ù)結構還包括list、set、pqueue等。

模板代碼

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

  • 指定HTTP方法、URL
  • 鑒權
  • 參數(shù)綁定
  • 處理請求
  • 處理響應

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

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

// 處理請求
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
		}
		// 進一步處理請求結構體
		if reqFunc != nil {
			reqFunc(c, req)
		}
		var rsp *Rsp
		// 業(yè)務邏輯函數(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")
		}
		// 處理響應
		ProcessRsp(c, rsp, err)
	}
}

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

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

代碼地址,實現(xiàn)了一個基于gin的RESTful風格模板。

對象池/緩存

Golang標準庫自帶了一個線程安全、高性能、還能夠根據(jù)對象熱度自動進行釋放的對象池sync.Pool,然而作為對象池,我們一般只會往里面放一種類型的對象,但sync.Pool里面的元素還是interface{}類型,因此我們可以簡單的封裝sync.Pool,讓它里面的元素有具體類型:

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

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

// 清理對象
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
}

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

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

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

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

代碼地址

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

總結

可以看到,其實Golang泛型主要提供了一種代碼抽象、封裝的能力,讓我們能夠寫出更能復用的代碼,避免代碼到處拷貝,從而能夠提高代碼的可維護性,可讀性,還能從避免類型轉換中得到一點性能提升。

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

相關文章

  • 手把手帶你走進Go語言之類型轉換

    手把手帶你走進Go語言之類型轉換

    每個函數(shù)都可以強制將一個表達式轉換成某種特定數(shù)據(jù)類型,本文給大家介紹了在Go語言中類型轉換的具體用法,講述的非常詳細,對大家的學習或工作具有一定的參考借鑒價值
    2021-09-09
  • Go語言利用aicli實現(xiàn)輕松調(diào)用DeepSeek和ChatGPT

    Go語言利用aicli實現(xiàn)輕松調(diào)用DeepSeek和ChatGPT

    這篇文章主要為大家介紹了一款用Go語言編寫的AI助手客戶端庫——aicli,該庫不僅支持ChatGPT,還集成了DeepSeek,感興趣的小伙伴可以了解一下
    2025-03-03
  • Golang中使用JSON的一些小技巧分享

    Golang中使用JSON的一些小技巧分享

    這篇文章主要分享了Golang中使用JSON的一些小技巧,文中通過示例代碼介紹的非常詳細,對大家具有一定的參考學習價值,需要的朋友們下面來一起看看吧。
    2017-06-06
  • 詳解Go語言中new和make關鍵字的區(qū)別

    詳解Go語言中new和make關鍵字的區(qū)別

    本篇文章來介紹一道非常常見的面試題,到底有多常見呢?可能很多面試的開場白就是由此開始的。那就是 new 和 make 這兩個內(nèi)置函數(shù)的區(qū)別,希望對大家有所幫助
    2023-03-03
  • CSP communicating sequential processes并發(fā)模型

    CSP communicating sequential processes并發(fā)模型

    這篇文章主要為大家介紹了CSP communicating sequential processes并發(fā)模型,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-05-05
  • Go 類型轉換工具包strconv包的用法

    Go 類型轉換工具包strconv包的用法

    Go 語言的?strconv?包提供了用于基本數(shù)據(jù)類型之間轉換的函數(shù),本文主要介紹了Go 類型轉換工具包strconv包的用法,具有一定的參考價值,感興趣的可以了解一下
    2024-05-05
  • Go框架自動化工具Beego使用詳解

    Go框架自動化工具Beego使用詳解

    這篇文章主要為大家介紹了Go框架自動化工具Beego使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-12-12
  • golang判斷key是否在map中的代碼

    golang判斷key是否在map中的代碼

    這篇文章主要介紹了golang判斷key是否在map中的代碼,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • Go 實現(xiàn)HTTP中間人代理的操作

    Go 實現(xiàn)HTTP中間人代理的操作

    這篇文章主要介紹了Go 實現(xiàn)HTTP中間人代理的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • Golang?依賴注入經(jīng)典解決方案uber/fx理論解析

    Golang?依賴注入經(jīng)典解決方案uber/fx理論解析

    這篇文章主要為大家介紹了Golang依賴注入經(jīng)典解決方案uber/fx理論解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-05-05

最新評論

安平县| 同德县| 北票市| 乳山市| 巴彦淖尔市| 寿光市| 秦安县| 平潭县| 贵港市| 惠安县| 麟游县| 岱山县| 新河县| 江陵县| 津市市| 鄂托克前旗| 抚州市| 安乡县| 邢台县| 广灵县| 新和县| 凤阳县| 行唐县| 宜昌市| 涞水县| 广东省| 体育| 德化县| 仪征市| 苗栗县| 广昌县| 乐清市| 喀什市| 定日县| 鹤峰县| 萨嘎县| 柳州市| 根河市| 仙游县| 玉田县| 叙永县|