<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          一個活躍在眾多 Go 項目中的編程模式

          共 2991字,需瀏覽 6分鐘

           ·

          2022-02-28 10:50

          點擊上方“Go語言進階學(xué)習(xí)”,進行關(guān)注

          回復(fù)“Go語言”即可獲贈從入門到進階共10本電子書

          潯陽地僻無音樂,終歲不聞絲竹聲。

          今天我們介紹一個在 Go 語言中非常流行的編程模式:函數(shù)式選項模式(Functional Options)。該模式解決的問題是,如何更動態(tài)靈活地為對象配置參數(shù)??赡茏x者不太明白該痛點,不急,我們將在下文詳細詳解。

          問題

          假設(shè)我們在代碼中定義了一個用戶的結(jié)構(gòu)體對象 User,它擁有以下屬性。

          type?User?struct?{
          ?ID??????string????//?必需項
          ?Name????string????//?必需項
          ?Age?????int???????//?非必需項
          ?Gender??bool??????//?非必需項
          }

          初始化該對象時,最簡單的方式是直接填充屬性值,例如

          u?:=?&User{ID:?"12glkui234d",?Name:?"菜刀",?Age:?18,?Gender:?true}

          但是這里存在一個問題:User 對象中的屬性并不一定都是可導(dǎo)出的,例如 User 有一個屬性字段為 password(首字母小寫,非導(dǎo)出),如果在其他模塊中需要構(gòu)造 User 對象,這樣就不能填充該 password 字段了。

          所以我們需要定義構(gòu)造 User 對象的函數(shù),首先能想到最簡單的構(gòu)造函數(shù)方式如下。

          func?NewUser(id,?name?string,?age?int,?gender?bool)?*User?{
          ?return?&User{
          ??ID:?????id,
          ??Name:???name,
          ??Age:????age,
          ??Gender:?gender,
          ?}
          }

          但是這樣也存在一些問題:對于 User 對象而言,只有 ID、Name 屬性是必須的,Age 與 Gender 為非必需項,且并不能設(shè)置默認值,例如 Age 的默認值為 0,Gender 的默認值是 false ,這顯然不太合理。

          面對該問題,我們可以采用的解決方案有哪些呢?

          方案一:多函數(shù)構(gòu)造

          我們能想到最粗暴地解決方法是:為每種參數(shù)情況設(shè)置一種構(gòu)造函數(shù)。如下代碼所示

          func?NewUser(id,?name?string)?*User?{
          ?return?&User{ID:?id,?Name:?name}
          }

          func?NewUserWithAge(id,?name?string,?age?int)?*User?{
          ?return?&User{ID:?id,?Name:?name,?Age:?age}
          }

          func?NewUserWithGender(id,?name?string,?gender?bool)?*User?{
          ?return?&User{ID:?id,?Name:?name,?Gender:?gender}
          }

          func?NewUserWithAgeGender(id,?name?string,?age?int,?gender?bool)?*User?{
          ?return?&User{ID:?id,?Name:?name,?Age:?age,?Gender:?gender}
          }

          這種方式適合參數(shù)較少且不易發(fā)生變化的情況。該方式在 Go 標準庫中也有使用,例如 net 包中的 Dial 和 DialTimeout 方法。

          func?Dial(network,?address?string)?(Conn,?error)?{}
          func?DialTimeout(network,?address?string,?timeout?time.Duration)?(Conn,?error)?{}

          但該方式的缺陷也很明顯:試想,如果構(gòu)造對象 User 增加了參數(shù)字段 Phone,那么我們需要新增多少個組合函數(shù)?

          方案二:配置化

          另外一種常見的方式是配置化,我們將所有可選的參數(shù)放入一個 Config 的配置結(jié)構(gòu)體中。

          type?User?struct?{
          ?ID???string
          ?Name?string
          ?Cfg??*Config
          }

          type?Config?struct?{
          ?Age????int
          ?Gender?bool
          }

          func?NewUser(id,?name?string,?cfg?*Config)?*User?{
          ?return?&User{ID:?id,?Name:?name,?Cfg:?cfg}
          }

          這樣,我們只需要一個 NewUser() ?函數(shù),不管之后增加多少配置選項,NewUser 函數(shù)都不會得到破壞。

          但是,這種方式,我們需要先構(gòu)造 Config 對象,這時候?qū)?Config 的構(gòu)造又回到了方案一中存在的問題。

          方案三:函數(shù)式選項模式

          面對這樣的問題,我們還可以選擇函數(shù)式選項模式。

          首先,我們定義一個 Option 函數(shù)類型

          type?Option?func(*User)

          然后,為每個屬性值定義一個返回 Option 函數(shù)的函數(shù)

          func?WithAge(age?int)?Option?{
          ?return?func(u?*User)?{
          ??u.Age?=?age
          ?}
          }

          func?WithGender(gender?bool)?Option?{
          ?return?func(u?*User)?{
          ??u.Gender?=?gender
          ?}
          }

          此時,我們將 User 對象的構(gòu)造函數(shù)改為如下所示

          func?NewUser(id,?name?string,?options?...Option)?*User?{
          ?u?:=?&User{ID:?id,?Name:?name}
          ?for?_,?option?:=?range?options?{
          ??option(u)
          ?}
          ?return?u
          }

          按照這種構(gòu)造方式,我們就可以這樣配置 User 對象了

          u?:=?NewUser("12glkui234d",?"菜刀",?WithAge(18),?WithGender(true))

          以后不管 User 增加任何參數(shù) XXX,我們只需要增加對應(yīng)的 WithXXX 函數(shù)即可,是不是非常地優(yōu)雅?

          Functional Options 這種編程模式,我們經(jīng)常能在各種項目中找到它的身影。例如,我在 tidb 項目中僅使用 opts ... 關(guān)鍵字搜索,就能看到這么多使用了 Functional Options 的代碼(截圖還未包括全部)。

          總結(jié)

          函數(shù)式選項模式解決了如何動態(tài)靈活地為對象配置參數(shù)的問題, 但是需要在合適的場景才使用它。

          當對象的配置參數(shù)復(fù)雜,例如可選參數(shù)多、非導(dǎo)入字段、參數(shù)可能隨版本增加等情況,這時函數(shù)式選項模式就可以很好地幫助到我們。

          -------------------?End?-------------------

          往期精彩文章推薦:

          歡迎大家點贊,留言,轉(zhuǎn)發(fā),轉(zhuǎn)載,感謝大家的相伴與支持

          想加入Go學(xué)習(xí)群請在后臺回復(fù)【入群

          萬水千山總是情,點個【在看】行不行

          瀏覽 49
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  天天日日夜夜 | 69成人精品国产 | 操骚B视频 | 日本一二三区视频 | 中文字幕在线观看免费 |