构造者模式(Builder Pattern)使用简单的对象一步一步构建成一个复杂的对象。这种设计模式属于创建者模式,它提供了一种创建对象的最佳方式。一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。例如,计算机是由 CPU、主板、内存、硬盘、显卡、机箱、显示器、键盘、鼠标等部件组装而成的,采购员不可能自己去组装计算机,而是将计算机的配置要求告诉计算机销售公司,计算机销售公司安排技术人员去组装计算机,然后再交给要买计算机的采购员。
产品(Product):
构造器接口(Builder):
具体构建者(Concrete Builder):
指挥者(Director):
其实在 Golang 中对于创建类参数比较多的对象的时候,我们常见的做法是必填参数直接传递,可选参数通过传递可变的方法进行创建。
package builder
import "fmt"
const (
defaultMaxTotal = 10
defaultMaxIdle = 9
defaultMinIdle = 1
)
// ResourcePoolConfig resource pool
type ResourcePoolConfig struct {
name string
maxTotal int
maxIdle int
minIdle int
}
// ResourcePoolConfigBuilder 用于构建 ResourcePoolConfig
type ResourcePoolConfigBuilder struct {
name string
maxTotal int
maxIdle int
minIdle int
}
// SetName SetName
func (b *ResourcePoolConfigBuilder) SetName(name string) error {
if name == "" {
return fmt.Errorf("name can not be empty")
}
b.name = name
return nil
}
// SetMinIdle SetMinIdle
func (b *ResourcePoolConfigBuilder) SetMinIdle(minIdle int) error {
if minIdle < 0 {
return fmt.Errorf("max tatal cannot < 0, input: %d", minIdle)
}
b.minIdle = minIdle
return nil
}
// SetMaxIdle SetMaxIdle
func (b *ResourcePoolConfigBuilder) SetMaxIdle(maxIdle int) error {
if maxIdle < 0 {
return fmt.Errorf("max tatal cannot < 0, input: %d", maxIdle)
}
b.maxIdle = maxIdle
return nil
}
// SetMaxTotal SetMaxTotal
func (b *ResourcePoolConfigBuilder) SetMaxTotal(maxTotal int) error {
if maxTotal <= 0 {
return fmt.Errorf("max tatal cannot <= 0, input: %d", maxTotal)
}
b.maxTotal = maxTotal
return nil
}
// Build Build
func (b *ResourcePoolConfigBuilder) Build() (*ResourcePoolConfig, error) {
if b.name == "" {
return nil, fmt.Errorf("name can not be empty")
}
// 设置默认值
if b.minIdle == 0 {
b.minIdle = defaultMinIdle
}
if b.maxIdle == 0 {
b.maxIdle = defaultMaxIdle
}
if b.maxTotal == 0 {
b.maxTotal = defaultMaxTotal
}
if b.maxTotal < b.maxIdle {
return nil, fmt.Errorf("max total(%d) cannot < max idle(%d)", b.maxTotal, b.maxIdle)
}
if b.minIdle > b.maxIdle {
return nil, fmt.Errorf("max idle(%d) cannot < min idle(%d)", b.maxIdle, b.minIdle)
}
return &ResourcePoolConfig{
name: b.name,
maxTotal: b.maxTotal,
maxIdle: b.maxIdle,
minIdle: b.minIdle,
}, nil
}
func TestBuilder(t *testing.T) {
tests := []struct {
name string
builder *ResourcePoolConfigBuilder
want *ResourcePoolConfig
wantErr bool
}{
{
name: "name empty",
builder: &ResourcePoolConfigBuilder{
name: "",
maxTotal: 0,
},
want: nil,
wantErr: true,
},
{
name: "maxIdle < minIdle",
builder: &ResourcePoolConfigBuilder{
name: "test",
maxTotal: 0,
maxIdle: 10,
minIdle: 20,
},
want: nil,
wantErr: true,
},
{
name: "success",
builder: &ResourcePoolConfigBuilder{
name: "test",
},
want: &ResourcePoolConfig{
name: "test",
maxTotal: defaultMaxTotal,
maxIdle: defaultMaxIdle,
minIdle: defaultMinIdle,
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := tt.builder.Build()
fmt.Printf("Build() error = %v, wantErr %v\n", err, tt.wantErr)
fmt.Println(got)
})
}
}
package builder
import "fmt"
const (
defaultMaxTotal = 10
defaultMaxIdle = 9
defaultMinIdle = 1
)
// ResourcePoolConfig resource pool
type ResourcePoolConfig struct {
name string
maxTotal int
maxIdle int
minIdle int
}
// ResourcePoolConfigOption resource pool
type ResourcePoolConfigOption struct {
maxTotal int
maxIdle int
minIdle int
}
// ResourcePoolConfigOptFunc to set option
type ResourcePoolConfigOptFunc func(option *ResourcePoolConfigOption)
// NewResourcePoolConfig NewResourcePoolConfig
func NewResourcePoolConfig(name string, opts ...ResourcePoolConfigOptFunc) (*ResourcePoolConfig, error) {
if name == "" {
return nil, fmt.Errorf("name can not be empty")
}
option := &ResourcePoolConfigOption{
maxTotal: 10,
maxIdle: 9,
minIdle: 1,
}
for _, opt := range opts {
opt(option)
}
if option.maxTotal < 0 || option.maxIdle < 0 || option.minIdle < 0 {
return nil, fmt.Errorf("args err, option: %v", option)
}
if option.maxTotal < option.maxIdle || option.minIdle > option.maxIdle {
return nil, fmt.Errorf("args err, option: %v", option)
}
return &ResourcePoolConfig{
name: name,
maxTotal: option.maxTotal,
maxIdle: option.maxIdle,
minIdle: option.minIdle,
}, nil
}
func TestBuilder(t *testing.T) {
type args struct {
name string
opts []ResourcePoolConfigOptFunc
}
tests := []struct {
name string
args args
want *ResourcePoolConfig
wantErr bool
}{
{
name: "name empty",
args: args{
name: "",
},
want: nil,
wantErr: true,
},
{
name: "success",
args: args{
name: "test",
opts: []ResourcePoolConfigOptFunc{
func(option *ResourcePoolConfigOption) {
option.minIdle = 2
},
func(option *ResourcePoolConfigOption) {
option.maxTotal = 100
},
},
},
want: &ResourcePoolConfig{
name: "test",
maxTotal: 10,
maxIdle: 9,
minIdle: 2,
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := NewResourcePoolConfig(tt.args.name, tt.args.opts...)
require.Equalf(t, tt.wantErr, err != nil, "error = %v, wantErr %v", err, tt.wantErr)
assert.Equal(t, tt.want, got)
})
}
}
...未完待续