简单工厂
由于 Go 本身是没有构造函数的,一般而言我们采用 NewName 的方式创建对象/接口,当它返回的是接口的时候,其实就是简单工厂模式
package factory
// IRuleConfigParser IRuleConfigParser
type IRuleConfigParser interface {
Parse(data []byte)
}
// jsonRuleConfigParser jsonRuleConfigParser
type jsonRuleConfigParser struct {
}
// Parse Parse
func (J jsonRuleConfigParser) Parse(data []byte) {
panic("implement me")
}
// yamlRuleConfigParser yamlRuleConfigParser
type yamlRuleConfigParser struct {
}
// Parse Parse
func (Y yamlRuleConfigParser) Parse(data []byte) {
panic("implement me")
}
// NewIRuleConfigParser NewIRuleConfigParser
func NewIRuleConfigParser(t string) IRuleConfigParser {
switch t {
case "json":
return jsonRuleConfigParser{}
case "yaml":
return yamlRuleConfigParser{}
}
return nil
}
单元测试
package factory
import (
"reflect"
"testing"
)
func TestNewIRuleConfigParser(t *testing.T) {
type args struct {
t string
}
tests := []struct {
name string
args args
want IRuleConfigParser
}{
{
name: "json",
args: args{t: "json"},
want: jsonRuleConfigParser{},
},
{
name: "yaml",
args: args{t: "yaml"},
want: yamlRuleConfigParser{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := NewIRuleConfigParser(tt.args.t); !reflect.DeepEqual(got, tt.want) {
t.Errorf("NewIRuleConfigParser() = %v, want %v", got, tt.want)
}
})
}
}
工厂方法
当对象的创建逻辑比较复杂,不只是简单的 new 一下就可以,而是要组合其他类对象,做各种初始化操作的时候,推荐使用工厂方法模式,将复杂的创建逻辑拆分到多个工厂类中,让每个工厂类都不至于过于复杂
// IRuleConfigParserFactory 工厂方法接口
type IRuleConfigParserFactory interface {
CreateParser() IRuleConfigParser
}
// yamlRuleConfigParserFactory yamlRuleConfigParser 的工厂类
type yamlRuleConfigParserFactory struct {
}
// CreateParser CreateParser
func (y yamlRuleConfigParserFactory) CreateParser() IRuleConfigParser {
return yamlRuleConfigParser{}
}
// jsonRuleConfigParserFactory jsonRuleConfigParser 的工厂类
type jsonRuleConfigParserFactory struct {
}
// CreateParser CreateParser
func (j jsonRuleConfigParserFactory) CreateParser() IRuleConfigParser {
return jsonRuleConfigParser{}
}
// NewIRuleConfigParserFactory 用一个简单工厂封装工厂方法
func NewIRuleConfigParserFactory(t string) IRuleConfigParserFactory {
switch t {
case "json":
return jsonRuleConfigParserFactory{}
case "yaml":
return yamlRuleConfigParserFactory{}
}
return nil
}
单元测试
package factory
import (
"reflect"
"testing"
)
func TestNewIRuleConfigParserFactory(t *testing.T) {
type args struct {
t string
}
tests := []struct {
name string
args args
want IRuleConfigParserFactory
}{
{
name: "json",
args: args{t: "json"},
want: jsonRuleConfigParserFactory{},
},
{
name: "yaml",
args: args{t: "yaml"},
want: yamlRuleConfigParserFactory{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := NewIRuleConfigParserFactory(tt.args.t); !reflect.DeepEqual(got, tt.want) {
t.Errorf("NewIRuleConfigParserFactory() = %v, want %v", got, tt.want)
}
})
}
}
抽象工厂
package factory
// IRuleConfigParser IRuleConfigParser
type IRuleConfigParser interface {
Parse(data []byte)
}
// jsonRuleConfigParser jsonRuleConfigParser
type jsonRuleConfigParser struct{}
// Parse Parse
func (j jsonRuleConfigParser) Parse(data []byte) {
panic("implement me")
}
// ISystemConfigParser ISystemConfigParser
type ISystemConfigParser interface {
ParseSystem(data []byte)
}
// jsonSystemConfigParser jsonSystemConfigParser
type jsonSystemConfigParser struct{}
// Parse Parse
func (j jsonSystemConfigParser) ParseSystem(data []byte) {
panic("implement me")
}
// IConfigParserFactory 工厂方法接口
type IConfigParserFactory interface {
CreateRuleParser() IRuleConfigParser
CreateSystemParser() ISystemConfigParser
}
type jsonConfigParserFactory struct{}
func (j jsonConfigParserFactory) CreateRuleParser() IRuleConfigParser {
return jsonRuleConfigParser{}
}
func (j jsonConfigParserFactory) CreateSystemParser() ISystemConfigParser {
return jsonSystemConfigParser{}
}
单元测试
package factory
import (
"reflect"
"testing"
)
func Test_jsonConfigParserFactory_CreateRuleParser(t *testing.T) {
tests := []struct {
name string
want IRuleConfigParser
}{
{
name: "json",
want: jsonRuleConfigParser{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
j := jsonConfigParserFactory{}
if got := j.CreateRuleParser(); !reflect.DeepEqual(got, tt.want) {
t.Errorf("CreateRuleParser() = %v, want %v", got, tt.want)
}
})
}
}
func Test_jsonConfigParserFactory_CreateSystemParser(t *testing.T) {
tests := []struct {
name string
want ISystemConfigParser
}{
{
name: "json",
want: jsonSystemConfigParser{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
j := jsonConfigParserFactory{}
if got := j.CreateSystemParser(); !reflect.DeepEqual(got, tt.want) {
t.Errorf("CreateSystemParser() = %v, want %v", got, tt.want)
}
})
}
}
DI 容器
golang 现有的依赖注入框架:
使用反射实现的: https://github.com/uber-go/dig
使用 generate 实现的: https://github.com/google/wire
下文将通过反射实现一个类似 dig 简单的 demo,在课程里面的例子是读取配置文件,然后进行生成,下面提供是通过 provider 进行构建依赖关系。
代码实现
package di
import (
"fmt"
"reflect"
)
// Container DI 容器
type Container struct {
// 假设一种类型只能有一个 provider 提供
providers map[reflect.Type] provider
// 缓存以生成的对象
results map[reflect.Type] reflect.Value
}
type provider struct {
value reflect.Value
params[] reflect.Type
}
// New 创建一个容器
func New() * Container {
return &Container {
providers: map[reflect.Type] provider {},
results: map[reflect.Type] reflect.Value {},
}
}
// isError 判断是否是 error 类型
func isError(t reflect.Type) bool {
if t.Kind() != reflect.Interface {
return false
}
return t.Implements(reflect.TypeOf(reflect.TypeOf(( * error)(nil)).Elem()))
}
// Provide 对象提供者,需要传入一个对象的工厂方法,后续会用于对象的创建
func(c * Container) Provide(constructor interface {}) error {
v: = reflect.ValueOf(constructor)
// 仅支持函数 provider
if v.Kind() != reflect.Func {
return fmt.Errorf("constructor must be a func")
}
vt: = v.Type()
// 获取参数
params: = make([] reflect.Type, vt.NumIn())
for i: = 0;i < vt.NumIn();i++{
params[i] = vt.In(i)
}
// 获取返回值
results: = make([] reflect.Type, vt.NumOut())
for i: = 0;i < vt.NumOut();i++{
results[i] = vt.Out(i)
}
provider: = provider {
value: v,
params: params,
}
// 保存不同类型的 provider
for _,
result: = range results {
// 判断返回值是不是 error
if isError(result) {
continue
}
if _, ok: = c.providers[result];
ok {
return fmt.Errorf("%s had a provider", result)
}
c.providers[result] = provider
}
return nil
}
// Invoke 函数执行入口
func(c * Container) Invoke(function interface {}) error {
v: = reflect.ValueOf(function)
// 仅支持函数 provider
if v.Kind() != reflect.Func {
return fmt.Errorf("constructor must be a func")
}
vt: = v.Type()
// 获取参数
var err error
params: = make([] reflect.Value, vt.NumIn())
for i: = 0;i < vt.NumIn();i++{
params[i], err = c.buildParam(vt.In(i))
if err != nil {
return err
}
}
v.Call(params)
// 获取 providers
return nil
}
// buildParam 构建参数
// 1. 从容器中获取 provider
// 2. 递归获取 provider 的参数值
// 3. 获取到参数之后执行函数
// 4. 将结果缓存并且返回结果
func(c * Container) buildParam(param reflect.Type)(val reflect.Value, err error) {
if result, ok: = c.results[param];
ok {
return result, nil
}
provider, ok: = c.providers[param]
if !ok {
return reflect.Value {}, fmt.Errorf("can not found provider: %s", param)
}
params: = make([] reflect.Value, len(provider.params))
for i, p: = range provider.params {
params[i], err = c.buildParam(p)
}
results: = provider.value.Call(params)
for _, result: = range results {
// 判断是否报错
if isError(result.Type()) && !result.IsNil() {
return reflect.Value {}, fmt.Errorf("%s call err: %+v", provider, result)
}
if !isError(result.Type()) && !result.IsNil() {
c.results[result.Type()] = result
}
}
return c.results[param], nil
}
我们这里的实现比较粗糙,但是作为一个 demo 理解 di 容器也足够了,和 dig 相比还缺少很多东西,并且有许多的问题,例如 依赖关系,一种类型如果有多个 provider 如何处理等等等等。
可以看到我们总共就三个函数:
Provide: 获取对象工厂,并且使用一个 map 将对象工厂保存
Invoke: 执行入口
buildParam: 核心逻辑,构建参数
从容器中获取 provider
递归获取 provider 的参数值
获取到参数之后执行函数
将结果缓存并且返回结果
例子:
package main
import (
"fmt"
di "github.com/mohuishou/go-design-pattern/02_factory/024_di"
)
// A 依赖关系 A -> B -> C
type A struct {
B * B
}
// NewA NewA
func NewA(b * B) * A {
return &A {
B: b,
}
}
// B B
type B struct {
C * C
}
// NewB NewB
func NewB(c * C) * B {
return &B {
C: c
}
}
// C C
type C struct {
Num int
}
// NewC NewC
func NewC() * C {
return &C {
Num: 1,
}
}
func main() {
container: = di.New()
if err: = container.Provide(NewA);err != nil {
panic(err)
}
if err: = container.Provide(NewB);err != nil {
panic(err)
}
if err: = container.Provide(NewC);err != nil {
panic(err)
}
err: = container.Invoke(func(a * A) {
fmt.Printf("%+v: %d", a, a.B.C.Num)
})
if err != nil {
panic(err)
}
}