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

詳解Go語(yǔ)言如何利用高階函數(shù)寫出優(yōu)雅的代碼

 更新時(shí)間:2023年01月05日 09:38:00   作者:nil  
高階函數(shù)(Hiher-order?Function)定義為:滿足下列條件之一的函數(shù):接收一個(gè)或多個(gè)函數(shù)作為參數(shù);返回值是一個(gè)函數(shù)。本文為大家介紹了如何利用高階函數(shù)寫出優(yōu)雅的代碼,希望對(duì)大家有所幫助

前言

go項(xiàng)目中經(jīng)常需要查詢db,按照以前java開(kāi)發(fā)經(jīng)驗(yàn),會(huì)根據(jù)查詢條件寫很多方法,如:

  • GetUserByUserID
  • GetUsersByName
  • GetUsersByAge

每一種查詢條件寫一個(gè)方法,這種方式對(duì)外是挺好的,對(duì)外遵循嚴(yán)格原則,讓每個(gè)對(duì)外的方法接口是明確的。但是對(duì)內(nèi)的話,應(yīng)該盡可能的通用,做到代碼復(fù)用,少寫代碼,讓代碼看起來(lái)更優(yōu)雅、整潔。

問(wèn)題

在review代碼的時(shí)候,針對(duì)上面3個(gè)方法,一般寫法是

func GetUserByUserID(ctx context.Context, userID int64) (*User, error){
    db := GetDB(ctx)
    var user User
    if userID > 0 {
        db = db.Where(`userID = ?`, userID)
    }
    if err := db.Model(&User{}).Find(&user).Err; err != nil {
        return nil, err
    }
    
    return user, nil
}

func GetUsersByName(ctx context.Context, name string) (*User, error){
    db := GetDB(ctx)
    var users []User
    if name != "" {
        db = db.Where(`name like '%%'`, name)
    }
    if err := db.Model(&User{}).Find(&users).Err; err != nil {
        return nil, err
    }
    
    return users, nil
}

func GetUsersByAge(ctx context.Context, age int64) (*User, error){
    db := GetDB(ctx)
    var user User
    if age > 0 {
        db = db.Where(`age = ?`, age)
    }
    if err := db.Model(&User{}).Find(&user).Err; err != nil {
        return nil, err
    }
    
    return user, nil
}

當(dāng)User表上字段有幾十個(gè)的時(shí)候,上面類似的方法會(huì)越來(lái)越多,代碼沒(méi)有做到復(fù)用。當(dāng)有Teacher表、Class表等其他表的時(shí)候,上面的查詢方法又要翻倍。

調(diào)用方也會(huì)寫的很死,參數(shù)固定。當(dāng)要增加一個(gè)查詢條件的時(shí)候,要么改原來(lái)的函數(shù),增加一個(gè)參數(shù),這樣其他調(diào)用的地方也都要改;要么新寫一個(gè)函數(shù),這樣函數(shù)越來(lái)越多,難以維護(hù)和閱讀。

上面是青銅寫法,針對(duì)這種情況,下面介紹幾種白銀、黃金、王者寫法

白銀

將入?yún)⒍x成一個(gè)結(jié)構(gòu)體

type UserParam struct {
    ID int64
    Name string
    Age int64
}

將入?yún)⒍挤旁赨serParam結(jié)構(gòu)體中

func GetUserInfo(ctx context.Context, info *UserParam) ([]*User, error) {    
        db := GetDB(ctx)    
        db = db.Model(&User{})
        var infos []*User
        if info.ID > 0 {
            db = db.Where("user_id = ?", info.ID)    
        }
        if info.Name != "" {       
            db = db.Where("user_name = ?", info.Name)    
        }    
        if info.Age > 0 {       
            db = db.Where("age = ?", info.Age)    
        } 
        if err := db.Find(&infos).Err; err != nil {
            return nil, err
        }
        
        return infos, nil
}

這個(gè)代碼寫到這里,相比最開(kāi)始的方法其實(shí)已經(jīng)好了不少,至少 dao 層的方法從很多個(gè)入?yún)⒆兂闪艘粋€(gè),調(diào)用方的代碼也可以根據(jù)自己的需要構(gòu)建參數(shù),不需要很多空占位符。但是存在的問(wèn)題也比較明顯:仍然有很多判空不說(shuō),還引入了一個(gè)多余的結(jié)構(gòu)體。如果我們就到此結(jié)束的話,多少有點(diǎn)遺憾。

另外,如果我們?cè)贁U(kuò)展一下業(yè)務(wù)場(chǎng)景,我們使用的不是等值查詢,而是多值查詢或者區(qū)間查詢,比如查詢 status in (a, b),那上面的代碼又怎么擴(kuò)展呢?是不是又要引入一個(gè)方法,方法繁瑣暫且不說(shuō),方法名叫啥都會(huì)讓我們糾結(jié)很久;或許可以嘗試把每個(gè)參數(shù)都從單值擴(kuò)展成數(shù)組,然后賦值的地方從 = 改為 in()的方式,所有參數(shù)查詢都使用 in 顯然對(duì)性能不是那么友好。

黃金

更高級(jí)的優(yōu)化方法,是使用高階函數(shù)。

type Option func(*gorm.DB)

定義 Option 是一個(gè)函數(shù),這個(gè)函數(shù)的入?yún)㈩愋褪?gorm.DB,返回值為空。

然后針對(duì)每一個(gè)需要查詢的字段,定義一個(gè)高階函數(shù)

func UserID(ID int64) Option {    
    return func(db *gorm.DB) {       
        db.Where("`id` = ?", ID)    
    } 
}

func Name(name int64) Option {    
    return func(db *gorm.DB) {       
        db.Where("`name` like %?%", name)    
    } 
}

func Age(age int64) Option {    
    return func(db *gorm.DB) {       
        db.Where("`age` = ?", age)    
    } 
}

返回值是Option類型。

這樣上面3個(gè)方法就可以合并成一個(gè)方法了

func GetUsersByCondition(ctx context.Context, opts ...Option)([]*User, error) {
    db := GetDB(ctx)
    for i:=range opts {
        opts[i](db)
    }
    var users []User
    if err := db.Model(&User{}).Find(&users).Err; err != nil {
        return nil, err
    }
    return users, nil
}

沒(méi)有對(duì)比就沒(méi)有傷害,通過(guò)和最開(kāi)始的方法比較,可以看到方法的入?yún)?strong>由多個(gè)不同類型的參數(shù)變成了一組相同類型的函數(shù),因此在處理這些參數(shù)的時(shí)候,也無(wú)需一個(gè)一個(gè)的判空,而是直接使用一個(gè) for 循環(huán)就搞定,相比之前已經(jīng)簡(jiǎn)潔了很多。

還可以擴(kuò)展其他查詢條件,比如IN,大于等

func UserIDs(IDs int64) Option {    
    return func(db *gorm.DB) {       
        db.Where("`id` in (?)", IDs)    
    } 
}

func AgeGT(age int64) Option {    
    return func(db *gorm.DB) {       
        db.Where("`age` > ?", age)    
    } 
}

而且這個(gè)查詢條件最終是轉(zhuǎn)換成Where條件,跟具體的表無(wú)關(guān),也就是說(shuō)這些定義是可以被其他表復(fù)用的。

王者

優(yōu)化到上述方法已經(jīng)可以了,但是王者一般會(huì)繼續(xù)優(yōu)化。

上述方法GetUsersByCondition只能查User表,能不能更通用一些,查任意表呢?分享GetUsersByCondition方法,發(fā)現(xiàn)如果要做到查任意表,有2個(gè)阻礙:

  • 表明是在方法中寫死的
  • 返回值定義的是[]*User,不能通用

針對(duì)第一個(gè)問(wèn)題,我們可以定義一個(gè)Option來(lái)實(shí)現(xiàn)

func TableName(tableName string) Option {
    return func(db *grom.DB) {
        db.Table(tableName)
    }
}

針對(duì)第二個(gè)問(wèn)題,可以將返回參數(shù)作為入?yún)ⅲㄟ^(guò)引用的方式傳進(jìn)來(lái)

func GetRecords(ctx context.Context, in any, opts ...Option) {
    db := GetDB(ctx)
    for i:=range opts {
        opts[i](db)
    }
    
    return db.Find(in).Err
}

// 調(diào)用:根據(jù)user name 和age 查詢users
var users []User
if err := GetRecords(ctx, &users, TableName("user"), Name("張三"), Age(18)); err != nil {
    // TODO
}

總結(jié)

這里通過(guò)對(duì) grom 查詢條件的抽象,大大簡(jiǎn)化了對(duì) DB 組合查詢的寫法,提升了代碼的簡(jiǎn)潔。

以上就是詳解Go語(yǔ)言如何利用高階函數(shù)寫出優(yōu)雅的代碼的詳細(xì)內(nèi)容,更多關(guān)于Go語(yǔ)言高階函數(shù)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Go語(yǔ)言文件讀寫操作案例詳解

    Go語(yǔ)言文件讀寫操作案例詳解

    這篇文章主要為大家介紹了Go語(yǔ)言文件讀寫操作案例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-09-09
  • golang 解析word文檔操作

    golang 解析word文檔操作

    這篇文章主要介紹了golang 解析word文檔操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-12-12
  • 利用GO語(yǔ)言實(shí)現(xiàn)多人聊天室實(shí)例教程

    利用GO語(yǔ)言實(shí)現(xiàn)多人聊天室實(shí)例教程

    聊天室的實(shí)現(xiàn)大家應(yīng)該都遇到過(guò),這篇文章主要給大家介紹了關(guān)于利用GO語(yǔ)言實(shí)現(xiàn)多人聊天室的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧。
    2018-03-03
  • go語(yǔ)言?http模型reactor示例詳解

    go語(yǔ)言?http模型reactor示例詳解

    這篇文章主要介紹了go語(yǔ)言?http模型reactor,接下來(lái)看一段基于reactor的示例,這里運(yùn)行通過(guò)?go?run?main.go,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2023-01-01
  • golang原生實(shí)現(xiàn)JWT的示例代碼

    golang原生實(shí)現(xiàn)JWT的示例代碼

    在Go中實(shí)現(xiàn)JWT驗(yàn)證,可以通過(guò)標(biāo)準(zhǔn)庫(kù)crypto/hmac、crypto/sha256和encoding/base64來(lái)編寫自己的JWT,本文就詳細(xì)的來(lái)介紹一下,感興趣的可以了解下
    2023-05-05
  • 基于Go+OpenCV實(shí)現(xiàn)人臉識(shí)別功能的詳細(xì)示例

    基于Go+OpenCV實(shí)現(xiàn)人臉識(shí)別功能的詳細(xì)示例

    OpenCV是一個(gè)強(qiáng)大的計(jì)算機(jī)視覺(jué)庫(kù),提供了豐富的圖像處理和計(jì)算機(jī)視覺(jué)算法,本文將向你介紹在Mac上安裝OpenCV的步驟,并演示如何使用Go的OpenCV綁定庫(kù)進(jìn)行人臉識(shí)別,需要的朋友可以參考下
    2023-07-07
  • 使用Go語(yǔ)言封裝實(shí)現(xiàn)郵件發(fā)送功能

    使用Go語(yǔ)言封裝實(shí)現(xiàn)郵件發(fā)送功能

    在現(xiàn)代 Web 開(kāi)發(fā)中,郵件發(fā)送功能是一個(gè)常見(jiàn)的需求,本文將介紹如何在 Go 語(yǔ)言中封裝一個(gè)通用的郵件發(fā)送包,支持驗(yàn)證碼發(fā)送和通用郵件發(fā)送,需要的可以參考下
    2025-03-03
  • Golang 串口通信的實(shí)現(xiàn)示例

    Golang 串口通信的實(shí)現(xiàn)示例

    串口通信是一種常見(jiàn)的硬件通信方式,用于在計(jì)算機(jī)和外部設(shè)備之間傳輸數(shù)據(jù),本文主要介紹了Golang 串口通信的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-03-03
  • Go Mongox輕松實(shí)現(xiàn)MongoDB的時(shí)間字段自動(dòng)填充

    Go Mongox輕松實(shí)現(xiàn)MongoDB的時(shí)間字段自動(dòng)填充

    這篇文章主要為大家詳細(xì)介紹了Go語(yǔ)言如何使用 mongox 庫(kù),在插入和更新數(shù)據(jù)時(shí)自動(dòng)填充時(shí)間字段,從而提升開(kāi)發(fā)效率并減少重復(fù)代碼,需要的可以參考下
    2025-02-02
  • Go?Ticker?周期性定時(shí)器用法及實(shí)現(xiàn)原理詳解

    Go?Ticker?周期性定時(shí)器用法及實(shí)現(xiàn)原理詳解

    這篇文章主要為大家介紹了Go?Ticker?周期性定時(shí)器用法及實(shí)現(xiàn)原理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08

最新評(píng)論

唐山市| 环江| 邵阳市| 伽师县| 肇庆市| 南川市| 汉寿县| 长沙县| 陈巴尔虎旗| 清涧县| 合山市| 林芝县| 灵寿县| 锡林浩特市| 资溪县| 淅川县| 阿拉善左旗| 巩留县| 张家界市| 梁平县| 腾冲县| 双辽市| 长春市| 杭州市| 墨玉县| 贡山| 塔城市| 湄潭县| 海口市| 嘉兴市| 吉林省| 绵阳市| 山东省| 通辽市| 忻城县| 阿荣旗| 江口县| 遂川县| 桦南县| 延川县| 枣强县|