自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Go1.18 泛型的好、壞亦或丑?

開發(fā) 后端
Go 泛型定了,有哪些好的使用場景,哪些不好的應(yīng)用場景,亦或哪些使用看起來丑?本文聊聊這個問題。

[[442529]]

大家好,我是程序員幽鬼。

Go 泛型定了,有哪些好的使用場景,哪些不好的應(yīng)用場景,亦或哪些使用看起來丑?本文聊聊這個問題。

1 簡介

泛型很棒,而且 Go 變得比以前更方便了。但是與可能非常有用的 channel 類似,我們不應(yīng)該僅僅因為它們存在就到處使用它們。

除了用于數(shù)據(jù)結(jié)構(gòu),泛型還有其他很好的應(yīng)用場景。當(dāng)然,也有一些不好的用例,比如泛型日志器。還有一些可以使用的解決方案,但相當(dāng)丑陋,還有一些東西真的很丑。

讓我們分別看一個例子!

2 好的應(yīng)用場景

我真正夢想在 Go 中做的以及我認(rèn)為我現(xiàn)在終于可以做的是 CRUD 端點的泛型提供程序:

  1. type Model interface { 
  2.     ID() string 
  3.  
  4. type DataProvider[MODEL Model] interface { 
  5.     FindByID(id string) (MODEL, error) 
  6.     List() ([]MODEL, error) 
  7.     Update(id string, model MODEL) error 
  8.     Insert(model MODEL) error 
  9.     Delete(id string) error 

這是一個大接口,你可以根據(jù)具體用例的需要縮短它,但是,為了完整性起見,我們暫時就這么寫。

現(xiàn)在你可以定義一個使用 DataProvider 的 HTTP 處理程序:

  1. type HTTPHandler[MODEL Model] struct { 
  2.     dataProvider DataProvider[MODEL] 
  3.  
  4. func (h HTTPHandler[MODEL]) FindByID(rw http.ResponseWriter, req *http.Request) { 
  5.     // validate request here 
  6.     id = // extract id here 
  7.     model, err := h.dataProvider.FindByID(id) 
  8.     if err != nil { 
  9.         // error handling here 
  10.         return 
  11.     } 
  12.     err = json.NewEncoder(rw).Encode(model) 
  13.     if err != nil { 
  14.         // error handling here 
  15.         return 
  16.     } 

如你所見,我們可以為每個方法實現(xiàn)一次,然后我們就完成了。我們甚至可以在事物的另一端創(chuàng)建一個客戶端,我們只需要為基本方法實現(xiàn)一次。

為什么我們在這里使用泛型而不是簡單的我們已經(jīng)定義的 Model 接口?

與在此處使用 Model 類型本身相比,泛型有一些優(yōu)點:

  • 使用泛型方法,DataProvider 根本不需要知道 Model,也不需要實現(xiàn)它。它可以簡單地提供非常強(qiáng)大的具體類型(但仍然可以為簡單的用例抽象)
  • 我們可以擴(kuò)展這個解決方案并使用具體類型進(jìn)行操作。讓我們看看插入或更新的驗證器會是什么樣子。
  1. type HTTPHandler[MODEL any] struct { 
  2.     dataProvider DataProvider[MODEL] 
  3.     InsertValidator func(new MODEL) error 
  4.     UpdateValidator func(old MODEL, new MODEL) error 

在這個驗證器中是泛型方法的真正優(yōu)勢所在。我們將解析 HTTP 請求,如果定義了自定義的 InsertValidator,那么我們可以使用它來驗證模型是否檢出,我們可以以類型安全的方式進(jìn)行并使用具體模型:

  1. type User struct { 
  2.     FirstName string 
  3.     LastName string 
  4.  
  5. func InsertValidator(u User) error { 
  6.     if u.FirstName == "" { ... }  
  7.     if u.LastName == "" { ... } 

所以我們有一個泛型的處理器,我們可以用自定義回調(diào)來調(diào)整它,它直接為我們獲取有效負(fù)載。沒有類型轉(zhuǎn)換。沒有 map。只有結(jié)構(gòu)體本身!

3 不好的應(yīng)用場景

一起看一個泛型日志器的例子:

  1. type GenericLogger[T any] interface { 
  2.     WithField(string, string) T 
  3.     Info(string) 

這本身還不是很有用。有更簡單的方法可以將鍵值字符串對添加到日志器,并且沒有日志器(據(jù)我所知)實際實現(xiàn)此接口。我們也不需要新的日志標(biāo)準(zhǔn)。如果我們想使用 logrus[1],我們必須這樣做:

  1. type GenericLogger[T any, FIELD map[string]interface{}] interface{ 
  2.     WithFields(M) T 
  3.     Info(string) 

如果我們添加自引用部分,這實際上可能由 logrus 日志器實現(xiàn)。但是,讓我們考慮在實際結(jié)構(gòu)體中使用它,例如某種處理程序:

  1. type MessageHandler[T GenericLogger[T], FIELD map[string]interface{}] struct { 
  2.     logger GenericLogger[T, FIELD] 

為了在結(jié)構(gòu)體中使用這個日志器,我們需要使我們的結(jié)構(gòu)體泛型,這僅適用于日志器。如果 MessageHandler 本身正在處理泛型消息,那將變成第三個類型參數(shù)!

到目前為止,甚至沒有辦法將其分配給具有泛型的變量。所以,盡管我們可以用一個接口來表示這個日志器很棒,但我實際上建議不要這樣做。而我最喜歡的日志庫 (zap[2]),由于其字段的性質(zhì),甚至無法用它來表示。

4 丑的場景

當(dāng)我使用泛型時,我發(fā)現(xiàn)缺少對在方法中引入新泛型參數(shù)的支持。雖然這可能有很好的理由,但它確實需要一些解決方法。讓我們想象一下我們想要將一個 map 簡化為一個整數(shù)。理想情況下,我們將通過使用返回新泛型參數(shù)的方法來完成此操作,然后我們可以簡單地提供 map reduce 函數(shù)。

那么,當(dāng)我們?nèi)匀幌胍苑盒头绞娇s小該 map 時,我們該怎么辦?既然沒有方法,那么讓我們創(chuàng)建一個方法:

  1. type GenericMap[KEY comparable, VALUE any] map[KEY]VALUEfunc (g GenericMap[KEY, VALUE]) Values() []VALUE { 
  2.     values := make([]VALUE, len(g)) 
  3.     for _, v := range g { 
  4.         values = append(values, v) 
  5.     } 
  6.     return values 
  7.  
  8. func Reduce[KEY comparable, VALUE anyRETURN any](g GenericMap[KEY, VALUE], callback func(RETURNKEY, VALUE "KEY comparable, VALUE any, RETURN any"RETURNRETURN { 
  9.     var r RETURN 
  10.     for k, v := range g { 
  11.         r = callback(r, k, v) 
  12.     } 
  13.     return r 

GenericMap 成為第一個參數(shù)或我們的 Reduce 函數(shù)。在這種情況下,你可以使用任何類型的 map 作為第一個參數(shù),而不是 GenericMap。然而,我想說明的一點是,如果這個方法本身是 GenericMap 的一部分,那就太好了。即使不是,我們?nèi)匀豢梢阅7逻@種行為??偟膩碚f,我可能仍會在某些用例中使用這種模式,即使它實際上很丑陋。

5 真的很丑

有時你可能想要使用工廠模式,它為你提供諸如 DataProviders 之類的東西。你可能希望在動態(tài)注冊的端點上獲取提供程序。所以你可以這樣做:

  1. type DataProviderFactory struct { 
  2.     dataProviders map[providerKey]any 
  3. func ProviderByName[MODEL Model](factory *DataProviderFactory, name string "MODEL Model") (DataProvider[MODEL], bool) { 
  4.         var m MODEL 
  5.     prov, has := factory.dataProviders[providerKey{namename, typ: reflect.TypeOf(m)}] 
  6.     if !has { 
  7.        return nil, false 
  8.     } 
  9.     return prov.(DataProvider[MODEL]), true  
  1. func RegisterProvider[MODEL Model](factory *DataProviderFactory, name string, p DataProvider[MODEL] "MODEL Model") { 
  2.     var m MODEL 
  3.     factory.dataProviders[providerKey{namename, typ: reflect.TypeOf(m)}] = p  

雖然這有效,雖然它可能有用,但它是很丑。它將丑陋(反射)與更丑陋(泛型)的東西結(jié)合在一起。

雖然從技術(shù)上講這應(yīng)該是類型安全的,但由于我們的復(fù)合鍵具有名稱和反射類型,它仍然很難看。我是否要把它放在生產(chǎn)代碼的任何地方,我會很糾結(jié)。

6 總結(jié)

雖然我喜歡泛型,但我認(rèn)為很難取得平衡,尤其是在開始的時候。所以我們需要確保記住它們?yōu)槭裁创嬖?,在什么情況下我們應(yīng)該使用它們,什么時候我們應(yīng)該避免它們!

原文鏈接:https://itnext.io/golang-1-18-generics-the-good-the-bad-the-ugly-5e9fa2520e76

參考資料

[1]logrus: https://github.com/sirupsen/logrus

[2]zap: https://github.com/uber-go/zap

本文轉(zhuǎn)載自微信公眾號「幽鬼」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系幽鬼公眾號。

 

責(zé)任編輯:武曉燕 來源: 幽鬼
相關(guān)推薦

2021-12-15 12:59:56

Go泛型版Beta1

2021-12-02 18:21:49

GoIP 包設(shè)計

2021-12-15 10:23:56

Go 1.18 Bet語言泛型

2021-11-01 12:41:39

Go

2022-01-26 09:02:57

GoCut方法

2022-02-11 21:01:18

GoNetip網(wǎng)絡(luò)庫

2022-07-12 06:17:43

GoogleGolang開發(fā)工作

2021-10-18 10:53:26

Go 代碼技術(shù)

2022-02-14 09:32:49

Go場景TryLock

2022-01-19 08:51:00

Module工作區(qū)Go

2021-10-29 10:55:07

Go 泛型語言

2021-12-03 18:29:31

GoAny 泛型

2022-03-18 18:00:00

編程語言泛型支持模糊測試

2021-12-27 18:27:18

GoTryLockJava

2022-01-10 11:33:17

Go測試軟件

2023-01-28 08:05:32

轉(zhuǎn)換Go泛型

2022-01-05 07:07:37

Go核心設(shè)計

2022-03-28 13:34:26

Go泛型部署泛型

2020-11-24 13:05:35

Go語言泛型

2021-09-29 18:17:30

Go泛型語言
點贊
收藏

51CTO技術(shù)棧公眾號