原文地址
我一直在想,我们应该如何在golang中应用我们宝贵的“设计模式”?
Go并不是面向对象类型编程语言。如果你是一个Java或c#开发人员,你会发现在你的代码中应用SOLID原则有很多相似之处。但是Go比较特殊而且特殊是有原因的。
我们将仔细研究如何在Go中实现一些著名的设计模式。
建造者模式
建造者模式背后的动机是逐块创建复杂对象。我们在本文的目标是提供api来逐步创建复杂的对象。
为什么要用建造者模式?
有些对象很容易用简单的构造函数创建,而另一些则需要大量的构造函数来组合创建。
什么时候需要建造者模式
当您不需要终端用户在构造函数的参数中提供大量的信息,而希望通过提供一种创建复杂对象的方法使他们创建对象变得的更为简单。
如何实现
1、建造者组成部分
2、建造者参数
3、函数
建造者组成
包含多个建造者共同来创建一个对象,有时候一个对象比较复杂而且创建的过程需要多个建造者类型。将构建复杂对象的构造函数分离为多个建造者可以让我们控制结构体属性,方便用户设置属性值。
代码
假设我们想要创建一个API来构建这样一个person对象:
func RunBuilderFacet() {
pb := NewPersonBuilder()
pb.Lives().
At("Bangalore").
WithPostalCode("560102").
Works().
As("Software Engineer").
For("IBM").
In("Bangalore").
WithSalary(150000)
person := pb.Build()
fmt.Println(person)
}
我们首先创建一个Person结构和3个建造者PersonBuilder,PersonAddressBuilder和PersonJobBuilder
注意,personaddressbuilder和PersonJobBuilder都引用了PersonBuilder
//Person struct
type Person struct {
// Personal details
name, address, pin string
// Job details
workAddress, company, position string
salary int
}
//PersonBuilder struct
type PersonBuilder struct {
person *Person
}
//PersonAddressBuilder facet of PersonBuilder
type PersonAddressBuilder struct {
PersonBuilder
}
//PersonJobBuilder facet of PersonBuilder
type PersonJobBuilder struct {
PersonBuilder
}
接下来,我们将声明这些建造者的构造函数。
//NewPersonBuilder constructor for PersonBuilder
func NewPersonBuilder() *PersonBuilder {
return &PersonBuilder{person: &Person{}}
}
//Lives chains to type *PersonBuilder and returns a *PersonAddressBuilder
func (b *PersonBuilder) Lives() *PersonAddressBuilder {
return &PersonAddressBuilder{*b}
}
//Works chains to type *PersonBuilder and returns a *PersonJobBuilder
func (b *PersonBuilder) Works() *PersonJobBuilder {
return &PersonJobBuilder{*b}
}
注意,Lives()和Works()在初始化时是如何引用PersonBuilder的
为什么这很重要
这将使我们能够设置Person的属性,它是PersonBuilder的一部分。
如何设置属性
我们将为PersonAddressBuilder和PersonJobBuilder创建一些行为/函数/方法,用于设置person的属性。
//At adds address to person
func (a *PersonAddressBuilder) At(address string) *PersonAddressBuilder {
a.person.address = address
return a
}
//WithPostalCode adds postal code to person
func (a *PersonAddressBuilder) WithPostalCode(pin string) *PersonAddressBuilder {
a.person.pin = pin
return a
}
//As adds position to person
func (j *PersonJobBuilder) As(position string) *PersonJobBuilder {
j.person.position = position
return j
}
//For adds company to person
func (j *PersonJobBuilder) For(company string) *PersonJobBuilder {
j.person.company = company
return j
}
//In adds company address to person
func (j *PersonJobBuilder) In(companyAddress string) *PersonJobBuilder {
j.person.workAddress = companyAddress
return j
}
//WithSalary adds salary to person
func (j *PersonJobBuilder) WithSalary(salary int) *PersonJobBuilder {
j.person.salary = salary
return j
}
最后,一个返回Person对象的“Build”方法。
//Build builds a person from PersonBuilder
func (b *PersonBuilder) Build() *Person {
return b.person
}
因此完整代码如下:
package main
import "fmt"
// Example - Builder facets
//Person struct
type Person struct {
// Personal details
name, address, pin string
// Job details
workAddress, company, position string
salary int
}
//PersonBuilder struct
type PersonBuilder struct {
person *Person
}
//PersonAddressBuilder facet of PersonBuilder
type PersonAddressBuilder struct {
PersonBuilder
}
//PersonJobBuilder facet of PersonBuilder
type PersonJobBuilder struct {
PersonBuilder
}
//NewPersonBuilder constructor for PersonBuilder
func NewPersonBuilder() *PersonBuilder {
return &PersonBuilder{person: &Person{}}
}
//Lives chains to type *PersonBuilder and returns a *PersonAddressBuilder
func (b *PersonBuilder) Lives() *PersonAddressBuilder {
return &PersonAddressBuilder{*b}
}
//Works chains to type *PersonBuilder and returns a *PersonJobBuilder
func (b *PersonBuilder) Works() *PersonJobBuilder {
return &PersonJobBuilder{*b}
}
//At adds address to person
func (a *PersonAddressBuilder) At(address string) *PersonAddressBuilder {
a.person.address = address
return a
}
//WithPostalCode adds postal code to person
func (a *PersonAddressBuilder) WithPostalCode(pin string) *PersonAddressBuilder {
a.person.pin = pin
return a
}
//As adds position to person
func (j *PersonJobBuilder) As(position string) *PersonJobBuilder {
j.person.position = position
return j
}
//For adds company to person
func (j *PersonJobBuilder) For(company string) *PersonJobBuilder {
j.person.company = company
return j
}
//In adds company address to person
func (j *PersonJobBuilder) In(companyAddress string) *PersonJobBuilder {
j.person.workAddress = companyAddress
return j
}
//WithSalary adds salary to person
func (j *PersonJobBuilder) WithSalary(salary int) *PersonJobBuilder {
j.person.salary = salary
return j
}
//Build builds a person from PersonBuilder
func (b *PersonBuilder) Build() *Person {
return b.person
}
//RunBuilderFacet example
func RunBuilderFacet() {
pb := NewPersonBuilder()
pb.Lives().
At("Bangalore").
WithPostalCode("560102").
Works().
As("Software Engineer").
For("IBM").
In("Bangalore").
WithSalary(150000)
person := pb.Build()
fmt.Println(person)
}
解析
一旦创建了一个PersonBuilder对象,我们就切换到Lives(),它返回一个PersonAddressBuilder,其中有At()和WithPostalCode()这样的成员函数,因为我们在所有函数定义中返回指针,我们可以将函数调用连在一起。关键的地方就是各建造者都引用person结构体,能够通过指针来完成对象的属性设置。
通过这种方式,我们可以在不同的构建器之间跳转,并使用它们对应的成员函数来创建复杂的对象。
相关文件:
建造者参数: https://devcharmander.medium.com/go-builder-pattern-builder-parameters-637cd3f46240
函数式建造者: https://medium.com/@devcharmander/go-builder-pattern-the-functional-way-e40f347017ce