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

Go?函數(shù)選項模式

 更新時間:2025年08月06日 09:25:17   作者:dreamw  
在?Go?語言中,函數(shù)選項模式(Functional?Options?Pattern)?是一種優(yōu)雅的設(shè)計模式,用于處理可選配置參數(shù),特別是當(dāng)配置項較多或可能變化時,它避免了冗長的構(gòu)造函數(shù)參數(shù)列表,提高了代碼的可讀性和可擴(kuò)展性,本文介紹Go函數(shù)選項模式,感興趣的朋友一起看看吧

熟悉 Python 開發(fā)的同學(xué)都知道,Python 有默認(rèn)參數(shù)的存在,使得我們在實例化一個對象的時候,可以根據(jù)需要來選擇性的覆蓋某些默認(rèn)參數(shù),以此來決定如何實例化對象。當(dāng)一個對象有多個默認(rèn)參數(shù)時,這個特性非常好用,能夠優(yōu)雅地簡化代碼。

而 Go 語言從語法上是不支持默認(rèn)參數(shù)的,所以為了實現(xiàn)既能通過默認(rèn)參數(shù)創(chuàng)建對象,又能通過傳遞自定義參數(shù)創(chuàng)建對象,我們就需要通過一些編程技巧來實現(xiàn)。對于這些程序開發(fā)中的常見問題,軟件行業(yè)的先行者們總結(jié)了許多解決常見場景編碼問題的最佳實踐,這些最佳實踐后來成為了我們所說的設(shè)計模式。其中選項模式在 Go 語言開發(fā)中會經(jīng)常用到。

核心思想

定義一個 Option 函數(shù)類型,接收目標(biāo)結(jié)構(gòu)體的指針
創(chuàng)建多個返回 Option 的配置函數(shù)(通常以 With 開頭)
在構(gòu)造函數(shù)中使用可變參數(shù)接收這些選項函數(shù)

通常我們有以下三種方法來實現(xiàn)通過默認(rèn)參數(shù)創(chuàng)建對象,以及通過傳遞自定義參數(shù)創(chuàng)建對象:

  • 使用多個構(gòu)造函數(shù)
  • 默認(rèn)參數(shù)選項
  • 選項模式

通過多構(gòu)造函數(shù)實現(xiàn)

第一種方式是通過多構(gòu)造函數(shù)實現(xiàn),下面是一個簡單例子:

package main
import "fmt"
const (
    defaultAddr = "127.0.0.1"
    defaultPort = 8000
)
type Server struct {
    Addr string
    Port int
}
func NewServer() *Server {
    return &Server{
        Addr: defaultAddr,
        Port: defaultPort,
    }
}
func NewServerWithOptions(addr string, port int) *Server {
    return &Server{
        Addr: addr,
        Port: port,
    }
}
func main() {
    s1 := NewServer()
    s2 := NewServerWithOptions("localhost", 8001)
    fmt.Println(s1)  // &{127.0.0.1 8000}
    fmt.Println(s2)  // &{localhost 8001}
}

這里我們?yōu)?Server 結(jié)構(gòu)體實現(xiàn)了兩個構(gòu)造函數(shù):

  • NewServer:無需傳遞參數(shù)即可直接返回 Server 對象
  • NewServerWithOptions :需要傳遞 addr 和 port 兩個參數(shù)來構(gòu)造 Server 對象

如果通過默認(rèn)參數(shù)創(chuàng)建的對象即可滿足需求,不需要對 Server 進(jìn)行定制時,我們可以使用 NewServer 來生成對象(s1)。而如果需要對 Server 進(jìn)行定制時,我們則可以使用 NewServerWithOptions 來生成對象(s2)。

通過默認(rèn)參數(shù)選項實現(xiàn)

另外一種實現(xiàn)默認(rèn)參數(shù)的方案,是為要生成的結(jié)構(gòu)體對象定義一個選項結(jié)構(gòu)體,用來生成要創(chuàng)建對象的默認(rèn)參數(shù),代碼實現(xiàn)如下:

package main
import "fmt"
const (
    defaultAddr = "127.0.0.1"
    defaultPort = 8000
)
type Server struct {
    Addr string
    Port int
}
type ServerOptions struct {
    Addr string
    Port int
}
func NewServerOptions() *ServerOptions {
    return &ServerOptions{
        Addr: defaultAddr,
        Port: defaultPort,
    }
}
func NewServerWithOptions(opts *ServerOptions) *Server {
    return &Server{
        Addr: opts.Addr,
        Port: opts.Port,
    }
}
func main() {
    s1 := NewServerWithOptions(NewServerOptions())
    s2 := NewServerWithOptions(&ServerOptions{
        Addr: "localhost",
        Port: 8001,
    })
    fmt.Println(s1)  // &{127.0.0.1 8000}
    fmt.Println(s2)  // &{localhost 8001}
}

我們?yōu)?Server 結(jié)構(gòu)體專門實現(xiàn)了一個 ServerOptions 用來生成默認(rèn)參數(shù),調(diào)用 NewServerOptions 函數(shù)即可獲得默認(rèn)參數(shù)配置,構(gòu)造函數(shù) NewServerWithOptions 接收一個 *ServerOptions 類型作為參數(shù)。所以我們可以通過以下兩種方式來完成功能:

  • 直接將調(diào)用 NewServerOptions 函數(shù)的返回值傳遞給 NewServerWithOptions 來實現(xiàn)通過默認(rèn)參數(shù)生成對象(s1)
  • 通過手動構(gòu)造 ServerOptions 配置來生成定制對象(s2)

通過選項模式實現(xiàn)

以上兩種方式雖然都能夠完成功能,但卻有以下缺點:

  • 通過多構(gòu)造函數(shù)實現(xiàn)的方案需要我們在實例化對象時分別調(diào)用不同的構(gòu)造函數(shù),代碼封裝性不強(qiáng),會給調(diào)用者增加使用負(fù)擔(dān)。
  • 通過默認(rèn)參數(shù)選項實現(xiàn)的方案需要我們預(yù)先構(gòu)造一個選項結(jié)構(gòu),當(dāng)使用默認(rèn)參數(shù)生成對象時代碼看起來比較冗余。

而選項模式可以讓我們更為優(yōu)雅地解決這個問題。代碼實現(xiàn)如下:

package main
import "fmt"
const (
    defaultAddr = "127.0.0.1"
    defaultPort = 8000
)
type Server struct {
    Addr string
    Port int
}
type ServerOptions struct {
    Addr string
    Port int
}
type ServerOption interface {
    apply(*ServerOptions)
}
type FuncServerOption struct {
    f func(*ServerOptions)
}
func (fo FuncServerOption) apply(option *ServerOptions) {
    fo.f(option)
}
func WithAddr(addr string) ServerOption {
    return FuncServerOption{
        f: func(options *ServerOptions) {
            options.Addr = addr
        },
    }
}
func WithPort(port int) ServerOption {
    return FuncServerOption{
        f: func(options *ServerOptions) {
            options.Port = port
        },
    }
}
func NewServer(opts ...ServerOption) *Server {
    options := ServerOptions{
        Addr: defaultAddr,
        Port: defaultPort,
    }
    for _, opt := range opts {
        opt.apply(&options)
    }
    return &Server{
        Addr: options.Addr,
        Port: options.Port,
    }
}
func main() {
    s1 := NewServer()
    s2 := NewServer(WithAddr("localhost"), WithPort(8001))
    s3 := NewServer(WithPort(8001))
    fmt.Println(s1)  // &{127.0.0.1 8000}
    fmt.Println(s2)  // &{localhost 8001}
    fmt.Println(s3)  // &{127.0.0.1 8001}
}

乍一看我們的代碼復(fù)雜了很多,但其實調(diào)用構(gòu)造函數(shù)生成對象的代碼復(fù)雜度是沒有改變的,只是定義上的復(fù)雜。

我們定義了 ServerOptions 結(jié)構(gòu)體用來配置默認(rèn)參數(shù)。因為 Addr 和 Port 都有默認(rèn)參數(shù),所以 ServerOptions 的定義和 Server 定義是一樣的。但有一定復(fù)雜性的結(jié)構(gòu)體中可能會有些參數(shù)沒有默認(rèn)參數(shù),必須讓用戶來配置,這時 ServerOptions 的字段就會少一些,大家可以按需定義。

同時,我們還定義了一個 ServerOption 接口和實現(xiàn)了此接口的 FuncServerOption 結(jié)構(gòu)體,它們的作用是讓我們能夠通過 apply 方法為 ServerOptions 結(jié)構(gòu)體單獨配置某項參數(shù)。

我們可以分別為每個默認(rèn)參數(shù)都定義一個 WithXXX 函數(shù)用來配置參數(shù),如這里定義的 WithAddr 和 WithPort ,這樣用戶就可以通過調(diào)用 WithXXX 函數(shù)來定制需要覆蓋的默認(rèn)參數(shù)。

此時默認(rèn)參數(shù)定義在構(gòu)造函數(shù) NewServer 中,構(gòu)造函數(shù)的接收一個不定長參數(shù),類型為 ServerOption,在構(gòu)造函數(shù)內(nèi)部通過一個 for 循環(huán)調(diào)用每個傳進(jìn)來的 ServerOption 對象的 apply 方法,將用戶配置的參數(shù)依次賦值給構(gòu)造函數(shù)內(nèi)部的默認(rèn)參數(shù)對象 options 中,以此來替換默認(rèn)參數(shù),for 循環(huán)執(zhí)行完成后,得到的 options 對象將是最終配置,將其屬性依次賦值給 Server 即可生成新的對象。

總結(jié)

通過 s2 和 s3 的打印結(jié)果可以發(fā)現(xiàn),使用選項模式實現(xiàn)的構(gòu)造函數(shù)更加靈活,相較于前兩種實現(xiàn),選項模式中我們可以自由的更改其中任意一項或多項默認(rèn)配置。

雖然選項模式確實會多寫一些代碼,但多數(shù)情況下這都是值得的。比如 Google 的 gRPC 框架 Go 語言實現(xiàn)中創(chuàng)建 gRPC server 的構(gòu)造函數(shù) NewServer 就使用了選項模式,感興趣的同學(xué)可以看下其源碼的實現(xiàn)思想其實和這里的示例程序如出一轍。

以上就是我關(guān)于 Golang 選項模式的一點經(jīng)驗,希望今天的分享能夠給你帶來一些幫助。

推薦閱讀

服務(wù)端渲染基礎(chǔ)

云原生灰度更新實踐

到此這篇關(guān)于Go 函數(shù)選項模式的文章就介紹到這了,更多相關(guān)go 函數(shù)選項模式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go語言中nil判斷的注意事項(最新推薦)

    Go語言中nil判斷的注意事項(最新推薦)

    本文給大家介紹Go語言中nil判斷的注意事項,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2025-06-06
  • 細(xì)說Go語言中空結(jié)構(gòu)體的奇妙用途

    細(xì)說Go語言中空結(jié)構(gòu)體的奇妙用途

    Go語言中,我們可以定義空結(jié)構(gòu)體,即沒有任何成員變量的結(jié)構(gòu)體,使用關(guān)鍵字?struct{}?來表示。這種結(jié)構(gòu)體似乎沒有任何用處,但實際上它在?Go?語言中的應(yīng)用非常廣泛,本文就來詳解講講
    2023-05-05
  • Go?Excelize?API源碼解析GetSheetFormatPr使用示例

    Go?Excelize?API源碼解析GetSheetFormatPr使用示例

    這篇文章主要為大家介紹了Go?Excelize?API源碼解析GetSheetFormatPr使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • 深入刨析Golang-map底層原理

    深入刨析Golang-map底層原理

    這篇文章主要介紹了深入刨析Golang-map底層原理,Go 語言的 map 的使用非常簡易, 但其內(nèi)部實現(xiàn)相對比較復(fù)雜,文中有相關(guān)的代碼示例,,需要的朋友可以參考下
    2023-05-05
  • golang gin框架獲取參數(shù)的操作

    golang gin框架獲取參數(shù)的操作

    這篇文章主要介紹了golang gin框架獲取參數(shù)的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Go語言協(xié)程通道使用的問題小結(jié)

    Go語言協(xié)程通道使用的問題小結(jié)

    本文主要介紹了Go語言協(xié)程通道使用的問題小結(jié),詳細(xì)的介紹了使用的一些重要問題,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-08-08
  • Golang中切片長度和容量的區(qū)別示例詳解

    Golang中切片長度和容量的區(qū)別示例詳解

    切片長度與容量在Go中很常見,切片長度是切片中可用元素的數(shù)量,而切片容量是從切片中第一個元素開始計算的底層數(shù)組中的元素數(shù)量,這篇文章主要給大家介紹了關(guān)于Golang中切片長度和容量區(qū)別的相關(guān)資料,需要的朋友可以參考下
    2024-01-01
  • golang 函數(shù)以及函數(shù)和方法的詳解及區(qū)別

    golang 函數(shù)以及函數(shù)和方法的詳解及區(qū)別

    這篇文章主要介紹了golang 函數(shù)以及函數(shù)和方法的區(qū)別的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • Golang中interface轉(zhuǎn)string輸出打印方法

    Golang中interface轉(zhuǎn)string輸出打印方法

    這篇文章主要給大家介紹了關(guān)于Golang中interface轉(zhuǎn)string輸出打印的相關(guān)資料,在go語言中interface轉(zhuǎn)string可以直接使用fmt提供的fmt函數(shù),文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-02-02
  • 使用Go語言解決Scan空格結(jié)束輸入問題

    使用Go語言解決Scan空格結(jié)束輸入問題

    這篇文章主要為大家介紹了使用Go語言來解決Scan空格結(jié)束輸入問題,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步
    2021-11-11

最新評論