[Go] Option選項設計模式 — — 編程方式基礎入門
全部代碼地址,歡迎??
- Github:
https://github.com/ziyifast/ziyifast-code_instruction/tree/main/go-demo/go-option
1 介紹
在 Go 開發中,我們經常遇到需要處理多參數配置的場景。傳統方法存在諸多痛點,例如:
問題一:參數過多且順序敏感,導致我們調用時難以閱讀,不知道每個參數背后對應的含義
// 參數過多且順序敏感
func NewServer(addr string, port int, timeout time.Duration, maxConns int, tls bool) {// ...
}// 調用時難以閱讀
srv := NewServer(":8080", 3306, 10*time.Second, 100, true)
問題二:新增改動,需要修改所有調用點
// 新增參數需修改所有調用點
func NewServer(..., enableLog bool) // 新增參數破壞現有代碼
這時就可以使用Go自帶的Option方式編程。
Go Option主要有以下優勢:
- ? 自描述性:命名選項明確參數含義
- ? 安全擴展:新增選項不影響現有調用
- ? 默認值處理:自動應用合理默認配置
- ? 參數驗證:可在選項函數中實現驗證邏輯
- ? 順序無關:任意順序傳遞選項參數
2 基礎入門
2.1 定義類結構以及Option
// 定義配置結構體
type Config struct {Timeout time.DurationMaxConn intTLS bool
}// 定義選項函數類型
type Option func(*Config)// 步驟3:實現構造函數
func NewConfig(opts ...Option) *Config {// 設置默認值cfg := &Config{Timeout: 10 * time.Second,MaxConn: 100,TLS: false,}// 應用所有選項for _, opt := range opts {//因為opt本身就是func,所以這里相當于調用函數,入參為cfg structopt(cfg)}return cfg
}
2.2 定義選項函數Withxx
// 帶參數的選項
func WithTimeout(t time.Duration) Option {return func(c *Config) {c.Timeout = t}
}// 無參數的選項(開關功能)
func WithTLS() Option {return func(c *Config) {c.TLS = true}
}// 帶驗證的選項
func WithMaxConn(n int) Option {return func(c *Config) {if n > 0 {c.MaxConn = n} // 否則保持默認值}
}
2.3 使用
// 只使用默認值
defaultCfg := NewConfig()// 覆蓋部分默認值
customCfg := NewConfig(WithTimeout(30*time.Second),WithMaxConn(200),
)// 啟用特定功能
secureCfg := NewConfig(WithTLS(),WithTimeout(15*time.Second),
)
全部代碼
package mainimport ("fmt""time"
)// 定義配置結構體
type Config struct {Timeout time.DurationMaxConn intTLS bool
}// 定義選項函數類型
type Option func(*Config)// 實現構造函數
func NewConfig(opts ...Option) *Config {// 設置默認值cfg := &Config{Timeout: 10 * time.Second,MaxConn: 100,TLS: false,}// 應用所有選項for _, opt := range opts {opt(cfg)}return cfg
}// 帶參數的選項
func WithTimeout(t time.Duration) Option {return func(c *Config) {c.Timeout = t}
}// 無參數的選項(開關功能)
func WithTLS() Option {return func(c *Config) {c.TLS = true}
}// 帶驗證的選項
func WithMaxConn(n int) Option {return func(c *Config) {if n > 0 {c.MaxConn = n} // 否則保持默認值}
}func main() {// 啟用特定功能secureCfg := NewConfig(WithTLS(),WithTimeout(15*time.Second),)fmt.Println(secureCfg)
}
3. 實戰使用
Go Option方式可以用在數據庫配置、HTTP服務配置、客戶端連接配置、日志系統配置等。這里以HTTP服務配置為例。
package mainimport ("fmt""time"
)type Option func(*ServerConfig)type ServerConfig struct {Addr stringReadTimeout time.DurationIdleTimeout time.DurationEnableCORS bool
}func NewServer(addr string, opts ...Option) *ServerConfig {cfg := &ServerConfig{Addr: addr,ReadTimeout: 5 * time.Second,IdleTimeout: 30 * time.Second,}for _, opt := range opts {opt(cfg)}return cfg
}// 組合選項:同時設置多個相關參數
func WithTimeouts(read, idle time.Duration) Option {return func(s *ServerConfig) {s.ReadTimeout = reads.IdleTimeout = idle}
}func EnableCORS() Option {return func(s *ServerConfig) {s.EnableCORS = true}
}func main() {// 使用示例server := NewServer(":8080",WithTimeouts(10*time.Second, 60*time.Second),EnableCORS(),)fmt.Println(server)
}
4. 進階(Option與鏈式調用結合)
在 Go 中我們可以結合 Option 模式和鏈式調用可以創建高度可讀、靈活的 API,實現優雅編程。這種方式尤其適用于復雜對象的配置。
核心思想:
- Option 模式:使用函數閉包封裝配置邏輯
- 鏈式調用:每個配置方法返回對象本身,支持連續調用
package mainimport "fmt"// 目標配置對象
type Server struct {host stringport inttimeout int // 秒tls bool
}// Option 函數類型:接收 *Server 的閉包
type Option func(*Server)// 鏈式包裝器(關鍵結構)
type ServerBuilder struct {options []Option
}// 創建 Builder 實例
func NewBuilder() *ServerBuilder {return &ServerBuilder{}
}// 鏈式方法:添加配置選項
func (b *ServerBuilder) WithHost(host string) *ServerBuilder {b.options = append(b.options, func(s *Server) {s.host = host})return b
}func (b *ServerBuilder) WithPort(port int) *ServerBuilder {b.options = append(b.options, func(s *Server) {s.port = port})return b
}func (b *ServerBuilder) WithTimeout(timeout int) *ServerBuilder {b.options = append(b.options, func(s *Server) {s.timeout = timeout})return b
}func (b *ServerBuilder) WithTLS(tls bool) *ServerBuilder {b.options = append(b.options, func(s *Server) {s.tls = tls})return b
}func (b *ServerBuilder) Build() *Server {// 設置默認值s := &Server{host: "localhost",port: 8080,timeout: 30,}// 應用所有配置函數for _, option := range b.options {option(s)}return s
}func main() {server := NewBuilder().WithHost("api.ziyi.com").WithPort(443).WithTimeout(60).WithTLS(true).Build()fmt.Printf("%+v\n", server)// 輸出:&{host:api.example.com port:443 timeout:60 tls:true}
}
總結
①概念:
Option 模式是 Go 語言中處理復雜配置的優雅解決方案,它通過:
- 功能選項(Functional Options)實現靈活的配置擴展
- 默認值機制減少調用方負擔
- 命名參數提高代碼可讀性
- 零成本擴展支持未來需求變化
②使用場景:
- 對象需要 5個以上 配置參數時
- 超過 3個可選 配置項時
- 配置可能有 合理默認值 時
- 需要 高頻擴展 配置的場景
- 開源庫/框架中需要提供 友好API 時
實踐tips:可以從簡單的配置對象開始,當可選參數超過3個或發現構造函數參數過多時,可考慮重構為Option模式。