国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

腳本之家,腳本語言編程技術及教程分享平臺!
分類導航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服務器之家 - 腳本之家 - Golang - Go編程模式:詳解函數式選項模式

Go編程模式:詳解函數式選項模式

2021-11-29 23:24polarisxu站長polaris Golang

Go 不是完全面向對象語言,有一些面向對象模式不太適合它。但經過這些年的發展,Go 有自己的一些模式。今天介紹一個常見的模式:函數式選項模式(Functional Options Pattern)。

Go編程模式:詳解函數式選項模式

大家好,我是 polarisxu。

Go 不是完全面向對象語言,有一些面向對象模式不太適合它。但經過這些年的發展,Go 有自己的一些模式。今天介紹一個常見的模式:函數式選項模式(Functional Options Pattern)。

01 什么是函數式選項模式

Go 語言沒有構造函數,一般通過定義 New 函數來充當構造函數。然而,如果結構有較多字段,要初始化這些字段,有很多種方式,但有一種方式認為是最好的,這就是函數式選項模式(Functional Options Pattern)。

函數式選項模式是一種在 Go 中構造結構體的模式,它通過設計一組非常有表現力和靈活的 API 來幫助配置和初始化結構體。

在 Uber 的 Go 語言規范中提到了該模式:

Functional options 是一種模式,在該模式中,你可以聲明一個不透明的 Option 類型,該類型在某些內部結構中記錄信息。你接受這些可變數量的選項,并根據內部結構上的選項記錄的完整信息進行操作。

將此模式用于構造函數和其他公共 API 中的可選參數,你預計這些參數需要擴展,尤其是在這些函數上已經有三個或更多參數的情況下。

02 一個示例

為了更好的理解該模式,我們通過一個例子來講解。

定義一個 Server 結構體:

  1. package main
  2.  
  3. type Server {
  4. host string
  5. port int
  6. }
  7.  
  8. func New(host string, port int) *Server {
  9. return &Server{host, port}
  10. }
  11.  
  12. func (s *Server) Start() error {
  13. }

如何使用呢?

  1. package main
  2.  
  3. import (
  4. "log"
  5. "server"
  6. )
  7.  
  8. func main() {
  9. svr := New("localhost", 1234)
  10. if err := svr.Start(); err != nil {
  11. log.Fatal(err)
  12. }
  13. }

但如果要擴展 Server 的配置選項,如何做?通常有三種做法:

為每個不同的配置選項聲明一個新的構造函數

定義一個新的 Config 結構體來保存配置信息

使用 Functional Option Pattern

做法 1:為每個不同的配置選項聲明一個新的構造函數

這種做法是為不同選項定義專有的構造函數。假如上面的 Server 增加了兩個字段:

  1. type Server {
  2.  
  3. host string
  4.  
  5. port int
  6.  
  7. timeout time.Duration
  8.  
  9. maxConn int
  10.  
  11. }

一般來說,host 和 port 是必須的字段,而 timeout 和 maxConn 是可選的,所以,可以保留原來的構造函數,而這兩個字段給默認值:

  1. func New(host string, port int) *Server {
  2.  
  3. return &Server{host, port, time.Minute, 100}
  4.  
  5. }

然后針對 timeout 和 maxConn 額外提供兩個構造函數:

  1. func NewWithTimeout(host string, port int, timeout time.Duration) *Server {
  2.  
  3. return &Server{host, port, timeout}
  4.  
  5. }
  6.  
  7. func NewWithTimeoutAndMaxConn(host string, port int, timeout time.Duration, maxConn int) *Server {
  8.  
  9. return &Server{host, port, timeout, maxConn}
  10.  
  11. }

這種方式配置較少且不太會變化的情況,否則每次你需要為新配置創建新的構造函數。在 Go 語言標準庫中,有這種方式的應用。比如 net 包中的 Dial 和 DialTimeout:

  1. func Dial(network, address string) (Conn, error)
  2.  
  3. func DialTimeout(network, address string, timeout time.Duration) (Conn, error)

做法 2:使用專門的配置結構體

這種方式也是很常見的,特別是當配置選項很多時。通常可以創建一個 Config 結構體,其中包含 Server 的所有配置選項。這種做法,即使將來增加更多配置選項,也可以輕松的完成擴展,不會破壞 Server 的 API。

  1. type Server {
  2. cfg Config
  3. }
  4.  
  5. type Config struct {
  6. Host string
  7. Port int
  8. Timeout time.Duration
  9. MaxConn int
  10. }
  11.  
  12. func New(cfg Config) *Server {
  13. return &Server{cfg}
  14. }

在使用時,需要先構造 Config 實例,對這個實例,又回到了前面 Server 的問題上,因為增加或刪除選項,需要對 Config 有較大的修改。如果將 Config 中的字段改為私有,可能需要定義 Config 的構造函數。。。

做法 3:使用 Functional Option Pattern

一個更好的解決方案是使用 Functional Option Pattern。

在這個模式中,我們定義一個 Option 函數類型:

  1. type Option func(*Server)

Option 類型是一個函數類型,它接收一個參數:*Server。然后,Server 的構造函數接收一個 Option 類型的不定參數:

  1. func New(options ...Option) *Server {
  2.  
  3. svr := &Server{}
  4.  
  5. for _, f := range options {
  6.  
  7. f(svr)
  8.  
  9. }
  10.  
  11. return svr
  12.  
  13. }

那選項如何起作用?需要定義一系列相關返回 Option 的函數:

  1. func WithHost(host string) Option {
  2.  
  3. return func(s *Server) {
  4.  
  5. s.host = host
  6.  
  7. }
  8.  
  9. }
  10.  
  11. func WithPort(port int) Option {
  12.  
  13. return func(s *Server) {
  14.  
  15. s.port = port
  16.  
  17. }
  18.  
  19. }
  20.  
  21. func WithTimeout(timeout time.Duration) Option {
  22.  
  23. return func(s *Server) {
  24.  
  25. s.timeout = timeout
  26.  
  27. }
  28.  
  29. }
  30.  
  31. func WithMaxConn(maxConn int) Option {
  32.  
  33. return func(s *Server) {
  34.  
  35. s.maxConn = maxConn
  36.  
  37. }
  38.  
  39. }

針對這種模式,客戶端類似這么使用:

  1. package main
  2.  
  3. import (
  4.  
  5. "log"
  6.  
  7. "server"
  8.  
  9. )
  10.  
  11. func main() {
  12.  
  13. svr := New(
  14.  
  15. WithHost("localhost"),
  16.  
  17. WithPort(8080),
  18.  
  19. WithTimeout(time.Minute),
  20.  
  21. WithMaxConn(120),
  22.  
  23. )
  24.  
  25. if err := svr.Start(); err != nil {
  26.  
  27. log.Fatal(err)
  28.  
  29. }
  30.  
  31. }

將來增加選項,只需要增加對應的 WithXXX 函數即可。

這種模式,在第三方庫中使用挺多,比如 github.com/gocolly/colly:

  1. type Collector {
  2.  
  3. // 省略...
  4.  
  5. }
  6.  
  7. func NewCollector(options ...CollectorOption) *Collector
  8.  
  9. // 定義了一系列 CollectorOpiton
  10.  
  11. type CollectorOption{
  12.  
  13. // 省略...
  14.  
  15. }
  16.  
  17. func AllowURLRevisit() CollectorOption
  18.  
  19. func AllowedDomains(domains ...string) CollectorOption
  20.  
  21. ...

不過 Uber 的 Go 語言編程規范中提到該模式時,建議定義一個 Option 接口,而不是 Option 函數類型。該 Option 接口有一個未導出的方法,然后通過一個未導出的 options 結構來記錄各選項。

Uber 的這個例子能看懂嗎?

  1. type options struct {
  2. cache bool
  3. logger *zap.Logger
  4. }
  5.  
  6. type Option interface {
  7. apply(*options)
  8. }
  9.  
  10. type cacheOption bool
  11.  
  12. func (c cacheOption) apply(opts *options) {
  13. opts.cache = bool(c)
  14. }
  15.  
  16. func WithCache(c bool) Option {
  17. return cacheOption(c)
  18. }
  19.  
  20. type loggerOption struct {
  21. Log *zap.Logger
  22. }
  23.  
  24. func (l loggerOption) apply(opts *options) {
  25. opts.logger = l.Log
  26. }
  27.  
  28. func WithLogger(log *zap.Logger) Option {
  29. return loggerOption{Log: log}
  30. }
  31.  
  32. // Open creates a connection.
  33. func Open(
  34. addr string,
  35. opts ...Option,
  36. ) (*Connection, error) {
  37. options := options{
  38. cache: defaultCache,
  39. logger: zap.NewNop(),
  40. }
  41.  
  42. for _, o := range opts {
  43. o.apply(&options)
  44. }
  45.  
  46. // ...
  47. }

03 總結

在實際項目中,當你要處理的選項比較多,或者處理不同來源的選項(來自文件、來自環境變量等)時,可以考慮試試函數式選項模式。

注意,在實際工作中,我們不應該教條的應用上面的模式,就像 Uber 中的例子,Open 函數并非只接受一個 Option 不定參數,因為 addr 參數是必須的。因此,函數式選項模式更多應該應用在那些配置較多,且有可選參數的情況。

參考文獻

https://golang.cafe/blog/golang-functional-options-pattern.html

https://github.com/uber-go/guide/blob/master/style.md#functional-options

原文鏈接:https://mp.weixin.qq.com/s/B-HZu1oZGseaOuNUznjJFA

延伸 · 閱讀

精彩推薦
  • GolangGolang中Bit數組的實現方式

    Golang中Bit數組的實現方式

    這篇文章主要介紹了Golang中Bit數組的實現方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    天易獨尊11682021-06-09
  • Golanggo語言制作端口掃描器

    go語言制作端口掃描器

    本文給大家分享的是使用go語言編寫的TCP端口掃描器,可以選擇IP范圍,掃描的端口,以及多線程,有需要的小伙伴可以參考下。 ...

    腳本之家3642020-04-25
  • Golanggolang如何使用struct的tag屬性的詳細介紹

    golang如何使用struct的tag屬性的詳細介紹

    這篇文章主要介紹了golang如何使用struct的tag屬性的詳細介紹,從例子說起,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看...

    Go語言中文網11352020-05-21
  • Golanggolang的httpserver優雅重啟方法詳解

    golang的httpserver優雅重啟方法詳解

    這篇文章主要給大家介紹了關于golang的httpserver優雅重啟的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,...

    helight2992020-05-14
  • Golanggolang 通過ssh代理連接mysql的操作

    golang 通過ssh代理連接mysql的操作

    這篇文章主要介紹了golang 通過ssh代理連接mysql的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    a165861639710342021-03-08
  • GolangGolang通脈之數據類型詳情

    Golang通脈之數據類型詳情

    這篇文章主要介紹了Golang通脈之數據類型,在編程語言中標識符就是定義的具有某種意義的詞,比如變量名、常量名、函數名等等,Go語言中標識符允許由...

    4272021-11-24
  • Golanggolang json.Marshal 特殊html字符被轉義的解決方法

    golang json.Marshal 特殊html字符被轉義的解決方法

    今天小編就為大家分享一篇golang json.Marshal 特殊html字符被轉義的解決方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧 ...

    李浩的life12792020-05-27
  • Golanggo日志系統logrus顯示文件和行號的操作

    go日志系統logrus顯示文件和行號的操作

    這篇文章主要介紹了go日志系統logrus顯示文件和行號的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    SmallQinYan12302021-02-02
主站蜘蛛池模板: 亚洲精品网址 | 毛片一区 | 伊人久久综合 | 18毛片| 黄色一级片免费播放 | 日韩中文一区二区三区 | 欧美久久综合 | 亚洲欧美激情精品一区二区 | 亚洲中午字幕 | 成人免费xxx在线观看 | 蜜桃视频一区二区 | 久久久99国产精品免费 | av伊人网| 亚洲 欧美 另类 综合 偷拍 | 国产精品久久久久久久久久新婚 | 日本免费三片免费观看 | 久久午夜精品 | 日韩福利影院 | 色欧美日韩 | 午夜视频 | 亚洲三级在线免费观看 | 成人久久久 | 中文字幕视频在线观看 | 91在线日韩| 日本不卡一区二区三区在线观看 | 国产在线欧美 | 日本福利视频 | 日韩综合一区二区 | 亚洲 中文 欧美 日韩 在线观看 | 久久er99热精品一区二区 | 欧美永久精品 | 在线一区 | 亚洲精品福利 | 成人免费网站在线 | 一区二区中文字幕 | 成人福利视频 | 日韩有码一区二区三区 | 99精品欧美一区二区三区综合在线 | 亚洲欧美日韩一区二区 | 精品一二区 | 夜夜天天|