在 Go 語言開發中,設計靈活、可擴充套件的 API 是一項重要技能。而 Option
模式是一種常見且有效的方法,它允許我們為函式提供可選引數,並使程式碼更加簡潔和可讀。
在這篇文章中,我將透過兩種常見的應用方式,展示如何使用 Option
模式建立可選配置和可選過濾的功能。
1. 可選配置(Optional Configuration)
可選配置是一種常見的使用場景,例如建立一個物件時,某些欄位是可選的,使用者可以選擇性地設定這些欄位。
程式碼示例:
package main import "fmt" type User struct { Name string Age int } // Option 型別,定義一個函式型別 type userOption func(u *User) // WithAge 返回一個設定 Age 的 Option func WithAge(age int) userOption { return func(u *User) { u.Age = age } } // WithName 返回一個設定 Name 的 Option func WithName(name string) userOption { return func(u *User) { u.Name = name } } // NewUser 使用可選的 Option 建立一個 User func NewUser(options ...userOption) *User { u := new(User) for _, option := range options { option(u) } return u }
在這個例子中,userOption
是一個函式型別,可以對 User
結構體進行操作。透過 WithAge
和 WithName
函式,我們可以生成相應的 userOption
,然後在建立 User
物件時使用這些可選配置項。
使用示例:
func main() { u1 := NewUser(WithName("xx"), WithAge(11)) fmt.Printf("%+v\n", u1) // 輸出:{Name:xx Age:11} }
在這個示例中,我們只需提供我們關心的配置項即可,未提供的配置項將保持預設值。這種方法使得 API 更加靈活,使用者可以選擇性地傳遞引數,而不需要定義一大堆建構函式。
main 函式:
u1
是一個指標,指向User
結構體在記憶體中的位置。
NewUser 函式:
User
結構體分配在記憶體中,假設地址為0x0010
。User
結構體中包含Name
和Age
欄位,Name
欄位指向儲存"xx"
的記憶體地址。
WithName 和 WithAge 函式:
WithName
函式將name
引數的值("xx"
)賦值給User
結構體的Name
欄位。WithAge
函式將age
引數的值(11
)賦值給User
結構體的Age
欄位。
2. 可選過濾(Optional Filtering)
Option
模式的另一個強大應用是在數據處理時進行可選過濾。例如,當我們有一組資料需要根據不同的條件進行過濾時,可以透過 Option
模式實現靈活的過濾條件組合。
程式碼示例:
package main type QueryOption interface { Apply(u []*User) []*User } type Where struct { Name string Age int } func (w *Where) Apply(u []*User) []*User { res := make([]*User, 0, len(u)) for _, v := range u { if v.Age == w.Age && v.Name == w.Name { res = append(res, v) } } return res } type Limit struct { Offset int Limit int } func (l *Limit) Apply(u []*User) []*User { right := l.Limit + l.Offset if right >= len(u) { return u } return u[l.Offset:right] } func Filter(u []*User, ops ...QueryOption) []*User { for _, op := range ops { u = op.Apply(u) } return u }
在這個例子中,我們定義了 QueryOption
介面,並透過 Where
和 Limit
兩個結構體實現了可選過濾功能。每個過濾條件都實現了 Apply
方法,用來對 User
列表進行操作。
使用示例:
func main() { u1 := NewUser(WithName("xx"), WithAge(11)) u2 := NewUser(WithName("yy"), WithAge(22)) u3 := NewUser(WithName("xx"), WithAge(11)) users := []*User{u1, u2, u3} // 過濾符合條件的資料 result := Filter(users, &Where{Name: "xx", Age: 11}, &Limit{Offset: 0, Limit: 2}) for _, v := range result { fmt.Printf("%+v\n", v) } }
這裏我們使用了兩個過濾條件:Where
和 Limit
,首先透過 Where
過濾出符合條件的使用者,然後透過 Limit
對結果集進行分頁。
Main函式:
u1
,u2
,u3
是User
結構體例項的指標。users
是一個包含u1
、u2
、u3
指標的切片。result
是經過過濾後的結果切片,包含u1
和u3
。
Filter函式:
展示了
Filter
函式如何應用Where
和Limit
策略來生成最終結果。
總結:
Option
模式是一種極其靈活的設計模式,在 Go 中有著廣泛的應用。它透過將配置項和過濾條件封裝成函式或介面,使得程式碼更加簡潔、靈活,並且易於擴充套件。無論是在建立物件時的可選配置,還是在數據處理時的可選過濾,Option
模式都能幫助我們構建更加靈活的 API。
透過 Option
模式,我們可以避免定義大量的建構函式或方法過載,並且可以輕鬆地新增新的配置項或過濾條件而不破壞現有程式碼。這種設計模式對於構建可維護的、可擴充套件的程式碼庫非常有用。
以下為程式碼整體架構圖: