Golang - 選項模式 vs 構建器模式
在使用 Golang 創建複雜對象時,常用的兩種模式是選項模式(Options pattern)和構建器模式(Builder pattern)。這兩種模式各有優缺點,選擇適合項目需求的模式取決於具體情況。
問題
假設我們想創建一個具有許多可選參數的複雜對象。一種方法是創建一個構造函數,該構造函數接受所有參數,併爲可選參數提供默認值。然而,這種方法有一些缺點:
-
- 很難記住參數的順序。
-
- 很難知道哪些參數是可選的,哪些是必需的。
-
- 構造函數可能會變得很長,難以閱讀。
解決方案:選項模式
選項模式可以用於創建具有許多可選參數的對象。在這種模式下,我們定義一個包含可選參數的結構體,並提供設置這些參數的方法。相比構建器模式,這種模式可以更簡潔,並且適用於參數較少的對象。
示例
在 Golang 中,可以使用函數選項(functional options)來實現選項模式。函數選項是以結構體作爲參數,並返回修改後的結構體的函數。以下是使用選項模式創建披薩對象的示例代碼:
type Pizza struct {
dough string
sauce string
cheese string
toppings []string
}
type PizzaOptions struct {
Dough string
Sauce string
Cheese string
Toppings []string
}
type PizzaOption func(*PizzaOptions)
func WithDough(dough string) PizzaOption {
return func(po *PizzaOptions) {
po.Dough = dough
}
}
func WithSauce(sauce string) PizzaOption {
return func(po *PizzaOptions) {
po.Sauce = sauce
}
}
func WithCheese(cheese string) PizzaOption {
return func(po *PizzaOptions) {
po.Cheese = cheese
}
}
func WithToppings(toppings []string) PizzaOption {
return func(po *PizzaOptions) {
po.Toppings = toppings
}
}
func NewPizza(options ...PizzaOption) *Pizza {
opts := &PizzaOptions{}
for _, option := range options {
option(opts)
}
pizza := &Pizza{
dough: opts.Dough,
sauce: opts.Sauce,
cheese: opts.Cheese,
toppings: opts.Toppings,
}
return pizza
}
在這個例子中,我們定義了Pizza
結構體和PizzaOptions
結構體,其中PizzaOptions
是一個包含可選參數的結構體。然後,我們定義了一系列函數來設置每個選項,比如WithDough
、WithSauce
和WithToppings
。這些函數返回一個PizzaOption
,用於設置PizzaOptions
結構體上對應的字段。最後,我們定義了一個NewPizza
函數,它接受任意數量的PizzaOptions
參數,並構造一個Pizza
對象。
func main() {
pizza := NewPizza(
WithDough("Regular"),
WithSauce("Tomato"),
WithCheese("Mozzarella"),
WithToppings([]string{"Pepperoni", "Olives", "Mushrooms"}),
)
println(pizza.dough)
println(pizza.sauce)
println(pizza.cheese)
println(pizza.toppings)
}
Options 模式可以是 Builder 模式的一個很好替代方案,用於創建具有許多可選參數的對象,特別是當對象的參數較少時。然而,對於具有許多參數的對象來說,Options 模式可能變得笨拙,因爲需要設置所有選項的函數數量可能會很大。
在 Golang 標準庫中的使用
Options 模式在 Golang 標準庫中被用於創建諸如 http.Request 對象之類的對象,該對象具有許多可選參數。http.NewRequest 函數接受方法、URL 和可選的 headers 和 body 等參數,返回一個新的 http.Request 對象。headers 和 body 是可選參數,可以使用函數選項來設置。
替代方案:Builder 模式
Builder 模式通過將複雜對象的構建與其表示分離,提供了對這些問題的解決方案。Builder 模式涉及以下組件:
-
- Builder 接口,定義構建對象的步驟。
-
- ConcreteBuilder 結構體,實現 Builder 接口並提供構建對象的方法。
-
- Director 結構體,使用 Builder 來構建對象。
示例
以下是在 Golang 中使用 Builder 模式實現的示例,使用了文章中提到的 pizza 對象:
type Pizza struct {
dough string
sauce string
cheese string
toppings []string
}
type PizzaBuilder interface {
SetDough(string) PizzaBuilder
SetSauce(string) PizzaBuilder
SetCheese(string) PizzaBuilder
SetToppings([]string) PizzaBuilder
Build() *Pizza
}
type ConcretePizzaBuilder struct {
pizza *Pizza
}
func NewConcretePizzaBuilder() *ConcretePizzaBuilder {
return &ConcretePizzaBuilder{pizza: &Pizza{}}
}
func (cpb *ConcretePizzaBuilder) SetDough(dough string) PizzaBuilder {
cpb.pizza.dough = dough
return cpb
}
func (cpb *ConcretePizzaBuilder) SetSauce(sauce string) PizzaBuilder {
cpb.pizza.sauce = sauce
return cpb
}
func (cpb *ConcretePizzaBuilder) SetCheese(cheese string) PizzaBuilder {
cpb.pizza.cheese = cheese
return cpb
}
func (cpb *ConcretePizzaBuilder) SetToppings(toppings []string) PizzaBuilder {
cpb.pizza.toppings = toppings
return cpb
}
func (cpb *ConcretePizzaBuilder) Build() *Pizza {
return cpb.pizza
}
type Director struct {
builder PizzaBuilder
}
func NewDirector(builder PizzaBuilder) *Director {
return &Director{builder: builder}
}
func (d *Director) Construct() *Pizza {
return d.builder.SetDough("Thin Crust").SetSauce("Tomato").SetCheese("Mozzarella").SetToppings([]string{"Mushrooms", "Olives", "Onions"}).Build()
}
在這個示例中,我們定義了Pizza
結構體和PizzaBuilder
接口。ConcretePizzaBuilder
結構體實現了PizzaBuilder
接口,並提供了構建Pizza
對象的方法。Director
結構體使用PizzaBuilder
來構建Pizza
對象。Director
結構體並不是嚴格必需的,但它提供了一種簡化構建Pizza
對象過程的方式。
我們可以使用Director
和ConcretePizzaBuilder
來創建一個Pizza
對象,如下所示:
builder := NewConcretePizzaBuilder()
director := NewDirector(builder)
pizza := director.Construct()
這將創建一個具有以下屬性的Pizza
對象:
-
• Dough: Thin Crust
-
• Sauce: Tomato
-
• Cheese: Mozzarella
-
• Toppings: Mushrooms, Olives, Onions
請注意,我們只需要指定要更改的屬性。所有其他屬性都被設置爲默認值。這使得創建具有許多可選參數的複雜對象變得更容易,無需記住參數的順序以及哪些參數是可選的,哪些是必需的。
在 Golang 標準庫中的使用
構建器模式並未在 Golang 標準庫中使用,但在 Golang 應用程序中,它是一種常用的模式,用於創建具有許多可選參數的複雜對象。Options 模式也被用作 Golang 應用程序中的替代方案,用於創建具有許多可選參數的對象。
結論
Options 模式是 Builder 模式的一種替代方案,可用於創建具有許多可選參數的對象。它比 Builder 模式更簡潔,但對於具有許多參數的對象可能會變得笨重。在 Golang 中,可以使用函數選項來實現 Options 模式。
Builder 模式是一種強大的模式,可以用於創建具有許多可選參數的複雜對象。它將對象的構建與表示分離,並提供了一種使用相同構建過程創建同一對象的不同表示的方式。在 Golang 中,Builder 模式可用於輕鬆創建複雜對象。
參考資料
- • "Design Patterns: Elements of Reusable Object-Oriented Software"(《設計模式:可複用面向對象軟件的基礎》)Erich Gamma、John Vlissides、Ralph Johnson、Richard Helm 著
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/DQlNDnbq22MI0eNFnAs4EA