Golang - 選項模式 vs 構建器模式

在使用 Golang 創建複雜對象時,常用的兩種模式是選項模式(Options pattern)和構建器模式(Builder pattern)。這兩種模式各有優缺點,選擇適合項目需求的模式取決於具體情況。

問題

假設我們想創建一個具有許多可選參數的複雜對象。一種方法是創建一個構造函數,該構造函數接受所有參數,併爲可選參數提供默認值。然而,這種方法有一些缺點:

    1. 很難記住參數的順序。
    1. 很難知道哪些參數是可選的,哪些是必需的。
    1. 構造函數可能會變得很長,難以閱讀。

解決方案:選項模式

選項模式可以用於創建具有許多可選參數的對象。在這種模式下,我們定義一個包含可選參數的結構體,並提供設置這些參數的方法。相比構建器模式,這種模式可以更簡潔,並且適用於參數較少的對象。

示例

在 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是一個包含可選參數的結構體。然後,我們定義了一系列函數來設置每個選項,比如WithDoughWithSauceWithToppings。這些函數返回一個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 模式涉及以下組件:

    1. Builder 接口,定義構建對象的步驟。
    1. ConcreteBuilder 結構體,實現 Builder 接口並提供構建對象的方法。
    1. 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對象過程的方式。

我們可以使用DirectorConcretePizzaBuilder來創建一個Pizza對象,如下所示:

builder := NewConcretePizzaBuilder()
director := NewDirector(builder)
pizza := director.Construct()

這將創建一個具有以下屬性的Pizza對象:

請注意,我們只需要指定要更改的屬性。所有其他屬性都被設置爲默認值。這使得創建具有許多可選參數的複雜對象變得更容易,無需記住參數的順序以及哪些參數是可選的,哪些是必需的。

在 Golang 標準庫中的使用

構建器模式並未在 Golang 標準庫中使用,但在 Golang 應用程序中,它是一種常用的模式,用於創建具有許多可選參數的複雜對象。Options 模式也被用作 Golang 應用程序中的替代方案,用於創建具有許多可選參數的對象。

結論

Options 模式是 Builder 模式的一種替代方案,可用於創建具有許多可選參數的對象。它比 Builder 模式更簡潔,但對於具有許多參數的對象可能會變得笨重。在 Golang 中,可以使用函數選項來實現 Options 模式。

Builder 模式是一種強大的模式,可以用於創建具有許多可選參數的複雜對象。它將對象的構建與表示分離,並提供了一種使用相同構建過程創建同一對象的不同表示的方式。在 Golang 中,Builder 模式可用於輕鬆創建複雜對象。

參考資料

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/DQlNDnbq22MI0eNFnAs4EA