Go设计模式
Go-设计模式
整理参考:
Go设计模式24-总结(更新完毕) - Mohuishou (lailin.xyz)
- 设计模式学习的重点是什么?
- 设计原则,以及设计模式的使用场景和优缺点,实现相对来说还没有那么重要
- 如果是常见的设计模式是武术招式,那么设计原则就是内功心法,没有内功心法那么招式套路也就是花架子
- 熟练掌握不同设计模式的使用场景可以帮助我们学会见招拆招,灵活应用而不是只会套路
- 最后设计模式不是银弹,不要拿着🔨就觉得哪里都像是钉子,不要过早优化,持续重构才是正道
单例模式-创建型
个人初级选手,先熟悉常用的设计模式,再了解不常用的设计模式。重点关注:单例、工厂、观察者模式。后面再学模板方法、责任链、策略模式。
代码实现
单例模式采用了 饿汉式 和 懒汉式 两种实现,个人其实更倾向于饿汉式的实现,简单,并且可以将问题及早暴露,懒汉式虽然支持延迟加载,但是这只是把冷启动时间放到了第一次使用的时候,并没有本质上解决问题,并且为了实现懒汉式还不可避免的需要加锁。
饿汉式
代码实现:
package singleton
// Singleton 饿汉式单例
type Singleton struct{}
var singleton *Singleton
func init() {
singleton = &Singleton{}
}
// GetInstance 获取实例
func GetInstance() *Singleton {
return singleton
}
单元测试:
package singleton_test
import (
"testing"
singleton "github.com/mohuishou/go-design-pattern/01_singleton"
"github.com/stretchr/testify/assert"
)
func TestGetInstance(t *testing.T) {
assert.Equal(t, singleton.GetInstance(), singleton.GetInstance())
}
func BenchmarkGetInstanceParallel(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
if singleton.GetInstance() != singleton.GetInstance() {
b.Errorf("test fail")
}
}
})
}
懒汉式(双重检测)
代码实现:
package singleton
import "sync"
var (
lazySingleton *Singleton
once = &sync.Once{}
)
// GetLazyInstance 懒汉式
func GetLazyInstance() *Singleton {
if lazySingleton == nil {
once.Do(func() {
lazySingleton = &Singleton{}
})
}
return lazySingleton
}
单元测试:
package singleton_test
import (
"testing"
singleton "github.com/mohuishou/go-design-pattern/01_singleton"
"github.com/stretchr/testify/assert"
)
func TestGetLazyInstance(t *testing.T) {
assert.Equal(t, singleton.GetLazyInstance(), singleton.GetLazyInstance())
}
func BenchmarkGetLazyInstanceParallel(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
if singleton.GetLazyInstance() != singleton.GetLazyInstance() {
b.Errorf("test fail")
}
}
})
}
测试结果
go test -benchmem -bench="." -v
工厂模式&DI容器-创建型
这篇文章代码比较多,包含了 简单工厂、工厂方法、抽象工厂、DI 容器的实现,由于内容具有关联性就不进行拆分了。
代码实现
简单工厂
由于 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 的参数值
- 获取到参数之后执行函数
- 将结果缓存并且返回结果
example
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)
}
}
观察者模式-行为型
笔记
代码实现
基础实现
Code
package observer
import "fmt"
// ISubject subject
type ISubject interface {
Register(observer IObserver)
Remove(observer IObserver)
Notify(msg string)
}
// IObserver 观察者
type IObserver interface {
Update(msg string)
}
// Subject Subject
type Subject struct {
observers []IObserver
}
// Register 注册
func (sub *Subject) Register(observer IObserver) {
sub.observers = append(sub.observers, observer)
}
// Remove 移除观察者
func (sub *Subject) Remove(observer IObserver) {
for i, ob := range sub.observers {
if ob == observer {
sub.observers = append(sub.observers[:i], sub.observers[i+1:]...)
}
}
}
// Notify 通知
func (sub *Subject) Notify(msg string) {
for _, o := range sub.observers {
o.Update(msg)
}
}
// Observer1 Observer1
type Observer1 struct{}
// Update 实现观察者接口
func (Observer1) Update(msg string) {
fmt.Printf("Observer1: %s", msg)
}
// Observer2 Observer2
type Observer2 struct{}
// Update 实现观察者接口
func (Observer2) Update(msg string) {
fmt.Printf("Observer2: %s", msg)
}
单元测试
package observer
import "testing"
func TestSubject_Notify(t *testing.T) {
sub := &Subject{}
sub.Register(&Observer1{})
sub.Register(&Observer2{})
sub.Notify("hi")
}
使用 Golang 实现 EventBus
我们实现一个支持以下功能的事件总线
- 异步不阻塞
- 支持任意参数值
Code
package eventbus
import (
"fmt"
"reflect"
"sync"
)
// Bus Bus
type Bus interface {
Subscribe(topic string, handler interface{}) error
Publish(topic string, args ...interface{})
}
// AsyncEventBus 异步事件总线
type AsyncEventBus struct {
handlers map[string][]reflect.Value
lock sync.Mutex
}
// NewAsyncEventBus new
func NewAsyncEventBus() *AsyncEventBus {
return &AsyncEventBus{
handlers: map[string][]reflect.Value{},
lock: sync.Mutex{},
}
}
// Subscribe 订阅
func (bus *AsyncEventBus) Subscribe(topic string, f interface{}) error {
bus.lock.Lock()
defer bus.lock.Unlock()
v := reflect.ValueOf(f)
if v.Type().Kind() != reflect.Func {
return fmt.Errorf("handler is not a function")
}
handler, ok := bus.handlers[topic]
if !ok {
handler = []reflect.Value{}
}
handler = append(handler, v)
bus.handlers[topic] = handler
return nil
}
// Publish 发布
// 这里异步执行,并且不会等待返回结果
func (bus *AsyncEventBus) Publish(topic string, args ...interface{}) {
handlers, ok := bus.handlers[topic]
if !ok {
fmt.Println("not found handlers in topic:", topic)
return
}
params := make([]reflect.Value, len(args))
for i, arg := range args {
params[i] = reflect.ValueOf(arg)
}
for i := range handlers {
go handlers[i].Call(params)
}
}
单元测试
package eventbus
import (
"fmt"
"testing"
"time"
)
func sub1(msg1, msg2 string) {
time.Sleep(1 * time.Microsecond)
fmt.Printf("sub1, %s %s\n", msg1, msg2)
}
func sub2(msg1, msg2 string) {
fmt.Printf("sub2, %s %s\n", msg1, msg2)
}
func TestAsyncEventBus_Publish(t *testing.T) {
bus := NewAsyncEventBus()
bus.Subscribe("topic:1", sub1)
bus.Subscribe("topic:1", sub2)
bus.Publish("topic:1", "test1", "test2")
bus.Publish("topic:1", "testA", "testB")
time.Sleep(1 * time.Second)
}
结果
=== RUN TestAsyncEventBus_Publish
sub2, testA testB
sub2, test1 test2
sub1, testA testB
sub1, test1 test2
--- PASS: TestAsyncEventBus_Publish (1.01s)