构造者模式(Builder Pattern)使用简单的对象一步一步构建成一个复杂的对象。这种设计模式属于创建者模式,它提供了一种创建对象的最佳方式。一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。例如,计算机是由 CPU、主板、内存、硬盘、显卡、机箱、显示器、键盘、鼠标等部件组装而成的,采购员不可能自己去组装计算机,而是将计算机的配置要求告诉计算机销售公司,计算机销售公司安排技术人员去组装计算机,然后再交给要买计算机的采购员。
主要组成部分:
-
产品(Product):
- 需要构建的复杂对象,通常是一个包含多个属性的类。
-
构造器接口(Builder):
- 定义了构建产品的接口,通常包括设置产品各个部分的方法。
-
具体构建者(Concrete Builder):
- 实现了构造器接口,负责具体产品的构建过程。通常还提供一个方法用于获取最终产品。
-
指挥者(Director):
- 负责管理构建过程,调用构建者的具体方法来构建产品。
GO:
其实在 Golang 中对于创建类参数比较多的对象的时候,我们常见的做法是必填参数直接传递,可选参数通过传递可变的方法进行创建。
方式一:使用 Go 编写建造者模式的代码其实会很长,这些是它的一个缺点,所以如果不是参数的校验逻辑很复杂的情况下一般我们在 Go 中不会采用这种方式,而会采用后面的另外一种方式
package builderimport "fmt"const (defaultMaxTotal = 10defaultMaxIdle = 9defaultMinIdle = 1
)// ResourcePoolConfig resource pool
type ResourcePoolConfig struct {name stringmaxTotal intmaxIdle intminIdle int
}// ResourcePoolConfigBuilder 用于构建 ResourcePoolConfig
type ResourcePoolConfigBuilder struct {name stringmaxTotal intmaxIdle intminIdle int
}// SetName SetName
func (b *ResourcePoolConfigBuilder) SetName(name string) error {if name == "" {return fmt.Errorf("name can not be empty")}b.name = namereturn 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 = minIdlereturn 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 = maxIdlereturn 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 = maxTotalreturn 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 stringbuilder *ResourcePoolConfigBuilderwant *ResourcePoolConfigwantErr 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)})}}
方式二:GO常用的参数传递方法
package builderimport "fmt"const (defaultMaxTotal = 10defaultMaxIdle = 9defaultMinIdle = 1
)// ResourcePoolConfig resource pool
type ResourcePoolConfig struct {name stringmaxTotal intmaxIdle intminIdle int
}// ResourcePoolConfigOption resource pool
type ResourcePoolConfigOption struct {maxTotal intmaxIdle intminIdle 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 stringopts []ResourcePoolConfigOptFunc}tests := []struct {name stringargs argswant *ResourcePoolConfigwantErr 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)})}
}
JAVA:
...未完待续