├── .github └── workflows │ └── go.yml ├── .gitignore ├── 01_singleton ├── img.jpg ├── readme.md ├── singleton.go ├── singleton_lazy.go ├── singleton_lazy_test.go └── singleton_test.go ├── 02_factory ├── 021_simple_factory │ ├── simple_factory.go │ └── simple_factory_test.go ├── 022_factory_method │ ├── factory_method.go │ └── factory_method_test.go ├── 023_abstract_factory │ ├── abstract_factory.go │ └── abstract_factory_test.go ├── 024_di │ ├── di.go │ └── example │ │ └── example.go └── readme.md ├── 03_builder ├── builder.go ├── builder_test.go ├── option.go └── option_test.go ├── 04_prototype ├── prototype.go └── prototype_test.go ├── 05_proxy ├── dynamic_proxy.go ├── dynamic_proxy_test.go ├── static_proxy.go └── static_proxy_test.go ├── 06_bridge ├── bridge.go └── bridge_test.go ├── 07_decorator ├── decorator.go └── decorator_test.go ├── 08_adapter ├── adapter.go ├── adapter_test.go └── readme.md ├── 09_facade ├── facade.go ├── facade_test.go └── readme.md ├── 10_composite ├── composite.go └── composite_test.go ├── 11_flyweight ├── flyweight.go └── flyweight_test.go ├── 12_observer ├── 121_observer │ ├── observer.go │ └── observer_test.go └── 122_eventbus │ ├── eventbus.go │ └── eventbus_test.go ├── 13_template ├── template.go └── template_test.go ├── 14_strategy ├── strategy.go └── strategy_test.go ├── 15_chain ├── chain.go └── chain_test.go ├── 16_state ├── state.go └── state_test.go ├── 17_iterator ├── iterator.go └── iterator_test.go ├── 18_visitor ├── visitor.go └── visitor_test.go ├── 19_memento ├── memento.go └── memento_test.go ├── 20_command ├── command.go ├── command_func.go ├── command_func_test.go └── command_test.go ├── 21_interpreter ├── interpreter.go └── interpreter_test.go ├── 22_mediator ├── mediator.go └── mediator_test.go ├── go.mod ├── go.sum ├── imgs ├── img01.jpg └── img02.jpg ├── readme.md └── readme_en.md /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | 9 | jobs: 10 | test: 11 | name: test 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Set up Go 1.x 15 | uses: actions/setup-go@v2 16 | with: 17 | go-version: ^1.15 18 | id: go 19 | 20 | - name: Check out code into the Go module directory 21 | uses: actions/checkout@v2 22 | 23 | - name: Get dependencies 24 | run: | 25 | go get -v -t -d ./... 26 | 27 | - name: Test 28 | run: go test -v -cover -coverprofile coverage.out ./... 29 | 30 | - name: coverage 31 | run: go tool cover -func=coverage.out 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/jetbrains,go,visualstudiocode 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=jetbrains,go,visualstudiocode 4 | 5 | ### Go ### 6 | # Binaries for programs and plugins 7 | *.exe 8 | *.exe~ 9 | *.dll 10 | *.so 11 | *.dylib 12 | 13 | # Test binary, built with `go test -c` 14 | *.test 15 | 16 | # Output of the go coverage tool, specifically when used with LiteIDE 17 | *.out 18 | 19 | # Dependency directories (remove the comment below to include it) 20 | # vendor/ 21 | 22 | ### Go Patch ### 23 | /vendor/ 24 | /Godeps/ 25 | 26 | ### JetBrains ### 27 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 28 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 29 | 30 | # User-specific stuff 31 | .idea/**/workspace.xml 32 | .idea/**/tasks.xml 33 | .idea/**/usage.statistics.xml 34 | .idea/**/dictionaries 35 | .idea/**/shelf 36 | 37 | # Generated files 38 | .idea/**/contentModel.xml 39 | 40 | # Sensitive or high-churn files 41 | .idea/**/dataSources/ 42 | .idea/**/dataSources.ids 43 | .idea/**/dataSources.local.xml 44 | .idea/**/sqlDataSources.xml 45 | .idea/**/dynamic.xml 46 | .idea/**/uiDesigner.xml 47 | .idea/**/dbnavigator.xml 48 | 49 | # Gradle 50 | .idea/**/gradle.xml 51 | .idea/**/libraries 52 | 53 | # Gradle and Maven with auto-import 54 | # When using Gradle or Maven with auto-import, you should exclude module files, 55 | # since they will be recreated, and may cause churn. Uncomment if using 56 | # auto-import. 57 | # .idea/artifacts 58 | # .idea/compiler.xml 59 | # .idea/jarRepositories.xml 60 | # .idea/modules.xml 61 | # .idea/*.iml 62 | # .idea/modules 63 | # *.iml 64 | # *.ipr 65 | 66 | # CMake 67 | cmake-build-*/ 68 | 69 | # Mongo Explorer plugin 70 | .idea/**/mongoSettings.xml 71 | 72 | # File-based project format 73 | *.iws 74 | 75 | # IntelliJ 76 | out/ 77 | 78 | # mpeltonen/sbt-idea plugin 79 | .idea_modules/ 80 | 81 | # JIRA plugin 82 | atlassian-ide-plugin.xml 83 | 84 | # Cursive Clojure plugin 85 | .idea/replstate.xml 86 | 87 | # Crashlytics plugin (for Android Studio and IntelliJ) 88 | com_crashlytics_export_strings.xml 89 | crashlytics.properties 90 | crashlytics-build.properties 91 | fabric.properties 92 | 93 | # Editor-based Rest Client 94 | .idea/httpRequests 95 | 96 | # Android studio 3.1+ serialized cache file 97 | .idea/caches/build_file_checksums.ser 98 | 99 | ### JetBrains Patch ### 100 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 101 | 102 | # *.iml 103 | # modules.xml 104 | # .idea/misc.xml 105 | # *.ipr 106 | 107 | # Sonarlint plugin 108 | # https://plugins.jetbrains.com/plugin/7973-sonarlint 109 | .idea/**/sonarlint/ 110 | 111 | # SonarQube Plugin 112 | # https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin 113 | .idea/**/sonarIssues.xml 114 | 115 | # Markdown Navigator plugin 116 | # https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced 117 | .idea/**/markdown-navigator.xml 118 | .idea/**/markdown-navigator-enh.xml 119 | .idea/**/markdown-navigator/ 120 | 121 | # Cache file creation bug 122 | # See https://youtrack.jetbrains.com/issue/JBR-2257 123 | .idea/$CACHE_FILE$ 124 | 125 | # CodeStream plugin 126 | # https://plugins.jetbrains.com/plugin/12206-codestream 127 | .idea/codestream.xml 128 | 129 | ### VisualStudioCode ### 130 | .vscode/* 131 | !.vscode/settings.json 132 | !.vscode/tasks.json 133 | !.vscode/launch.json 134 | !.vscode/extensions.json 135 | *.code-workspace 136 | 137 | ### VisualStudioCode Patch ### 138 | # Ignore all local history of files 139 | .history 140 | 141 | # End of https://www.toptal.com/developers/gitignore/api/jetbrains,go,visualstudiocode 142 | 143 | test.* 144 | .idea/ -------------------------------------------------------------------------------- /01_singleton/img.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohuishou/go-design-pattern/2dd60236ec711b599a935f9bd00a0e848e982929/01_singleton/img.jpg -------------------------------------------------------------------------------- /01_singleton/readme.md: -------------------------------------------------------------------------------- 1 | # 单例模式(Singleton Design Pattern) 2 | 3 | ![img.jpg](./img.jpg) 4 | 5 | 单例模式采用了 饿汉式 和 懒汉式 两种实现,个人其实更倾向于饿汉式的实现,简单,并且可以将问题及早暴露,懒汉式虽然支持延迟加载,但是这只是把冷启动时间放到了第一次使用的时候,并没有本质上解决问题,并且为了实现懒汉式还不可避免的需要加锁 6 | -------------------------------------------------------------------------------- /01_singleton/singleton.go: -------------------------------------------------------------------------------- 1 | package singleton 2 | 3 | // Singleton 饿汉式单例 4 | type Singleton struct { 5 | a string // 测试字段 6 | } 7 | 8 | var singleton *Singleton 9 | 10 | func init() { 11 | singleton = &Singleton{a: "test"} 12 | } 13 | 14 | // GetInstance 获取实例 15 | func GetInstance() *Singleton { 16 | return singleton 17 | } 18 | -------------------------------------------------------------------------------- /01_singleton/singleton_lazy.go: -------------------------------------------------------------------------------- 1 | package singleton 2 | 3 | import "sync" 4 | 5 | var ( 6 | lazySingleton *Singleton 7 | once = &sync.Once{} 8 | ) 9 | 10 | // GetLazyInstance 懒汉式 11 | func GetLazyInstance() *Singleton { 12 | // once 内的方法只会执行一次,所以不需要再次判断 13 | once.Do(func() { 14 | lazySingleton = &Singleton{a: "test"} 15 | }) 16 | 17 | return lazySingleton 18 | } 19 | -------------------------------------------------------------------------------- /01_singleton/singleton_lazy_test.go: -------------------------------------------------------------------------------- 1 | package singleton_test 2 | 3 | import ( 4 | "testing" 5 | 6 | singleton "github.com/mohuishou/go-design-pattern/01_singleton" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestGetLazyInstance(t *testing.T) { 12 | assert.Equal(t, singleton.GetLazyInstance(), singleton.GetLazyInstance()) 13 | } 14 | 15 | func BenchmarkGetLazyInstanceParallel(b *testing.B) { 16 | b.RunParallel(func(pb *testing.PB) { 17 | for pb.Next() { 18 | if singleton.GetLazyInstance() != singleton.GetLazyInstance() { 19 | b.Errorf("test fail") 20 | } 21 | } 22 | }) 23 | } 24 | -------------------------------------------------------------------------------- /01_singleton/singleton_test.go: -------------------------------------------------------------------------------- 1 | package singleton_test 2 | 3 | import ( 4 | "testing" 5 | 6 | singleton "github.com/mohuishou/go-design-pattern/01_singleton" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestGetInstance(t *testing.T) { 12 | assert.True(t, singleton.GetInstance() == singleton.GetInstance()) 13 | assert.False(t, singleton.GetInstance() == singleton.GetLazyInstance()) 14 | } 15 | 16 | func BenchmarkGetInstanceParallel(b *testing.B) { 17 | b.RunParallel(func(pb *testing.PB) { 18 | for pb.Next() { 19 | if singleton.GetInstance() != singleton.GetInstance() { 20 | b.Errorf("test fail") 21 | } 22 | } 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /02_factory/021_simple_factory/simple_factory.go: -------------------------------------------------------------------------------- 1 | package factory 2 | 3 | // IRuleConfigParser IRuleConfigParser 4 | type IRuleConfigParser interface { 5 | Parse(data []byte) 6 | } 7 | 8 | // jsonRuleConfigParser jsonRuleConfigParser 9 | type jsonRuleConfigParser struct { 10 | } 11 | 12 | // Parse Parse 13 | func (J jsonRuleConfigParser) Parse(data []byte) { 14 | panic("implement me") 15 | } 16 | 17 | // yamlRuleConfigParser yamlRuleConfigParser 18 | type yamlRuleConfigParser struct { 19 | } 20 | 21 | // Parse Parse 22 | func (Y yamlRuleConfigParser) Parse(data []byte) { 23 | panic("implement me") 24 | } 25 | 26 | // NewIRuleConfigParser NewIRuleConfigParser 27 | func NewIRuleConfigParser(t string) IRuleConfigParser { 28 | switch t { 29 | case "json": 30 | return jsonRuleConfigParser{} 31 | case "yaml": 32 | return yamlRuleConfigParser{} 33 | } 34 | return nil 35 | } 36 | -------------------------------------------------------------------------------- /02_factory/021_simple_factory/simple_factory_test.go: -------------------------------------------------------------------------------- 1 | package factory 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestNewIRuleConfigParser(t *testing.T) { 9 | type args struct { 10 | t string 11 | } 12 | tests := []struct { 13 | name string 14 | args args 15 | want IRuleConfigParser 16 | }{ 17 | { 18 | name: "json", 19 | args: args{t: "json"}, 20 | want: jsonRuleConfigParser{}, 21 | }, 22 | { 23 | name: "yaml", 24 | args: args{t: "yaml"}, 25 | want: yamlRuleConfigParser{}, 26 | }, 27 | } 28 | for _, tt := range tests { 29 | t.Run(tt.name, func(t *testing.T) { 30 | if got := NewIRuleConfigParser(tt.args.t); !reflect.DeepEqual(got, tt.want) { 31 | t.Errorf("NewIRuleConfigParser() = %v, want %v", got, tt.want) 32 | } 33 | }) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /02_factory/022_factory_method/factory_method.go: -------------------------------------------------------------------------------- 1 | package factory 2 | 3 | // IRuleConfigParser IRuleConfigParser 4 | type IRuleConfigParser interface { 5 | Parse(data []byte) 6 | } 7 | 8 | // jsonRuleConfigParser jsonRuleConfigParser 9 | type jsonRuleConfigParser struct { 10 | } 11 | 12 | // Parse Parse 13 | func (J jsonRuleConfigParser) Parse(data []byte) { 14 | panic("implement me") 15 | } 16 | 17 | // yamlRuleConfigParser yamlRuleConfigParser 18 | type yamlRuleConfigParser struct { 19 | } 20 | 21 | // Parse Parse 22 | func (Y yamlRuleConfigParser) Parse(data []byte) { 23 | panic("implement me") 24 | } 25 | 26 | // IRuleConfigParserFactory 工厂方法接口 27 | type IRuleConfigParserFactory interface { 28 | CreateParser() IRuleConfigParser 29 | } 30 | 31 | // yamlRuleConfigParserFactory yamlRuleConfigParser 的工厂类 32 | type yamlRuleConfigParserFactory struct { 33 | } 34 | 35 | // CreateParser CreateParser 36 | func (y yamlRuleConfigParserFactory) CreateParser() IRuleConfigParser { 37 | return yamlRuleConfigParser{} 38 | } 39 | 40 | // jsonRuleConfigParserFactory jsonRuleConfigParser 的工厂类 41 | type jsonRuleConfigParserFactory struct { 42 | } 43 | 44 | // CreateParser CreateParser 45 | func (j jsonRuleConfigParserFactory) CreateParser() IRuleConfigParser { 46 | return jsonRuleConfigParser{} 47 | } 48 | 49 | // NewIRuleConfigParserFactory 用一个简单工厂封装工厂方法 50 | func NewIRuleConfigParserFactory(t string) IRuleConfigParserFactory { 51 | switch t { 52 | case "json": 53 | return jsonRuleConfigParserFactory{} 54 | case "yaml": 55 | return yamlRuleConfigParserFactory{} 56 | } 57 | return nil 58 | } 59 | -------------------------------------------------------------------------------- /02_factory/022_factory_method/factory_method_test.go: -------------------------------------------------------------------------------- 1 | package factory 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestNewIRuleConfigParserFactory(t *testing.T) { 9 | type args struct { 10 | t string 11 | } 12 | tests := []struct { 13 | name string 14 | args args 15 | want IRuleConfigParserFactory 16 | }{ 17 | { 18 | name: "json", 19 | args: args{t: "json"}, 20 | want: jsonRuleConfigParserFactory{}, 21 | }, 22 | { 23 | name: "yaml", 24 | args: args{t: "yaml"}, 25 | want: yamlRuleConfigParserFactory{}, 26 | }, 27 | } 28 | for _, tt := range tests { 29 | t.Run(tt.name, func(t *testing.T) { 30 | if got := NewIRuleConfigParserFactory(tt.args.t); !reflect.DeepEqual(got, tt.want) { 31 | t.Errorf("NewIRuleConfigParserFactory() = %v, want %v", got, tt.want) 32 | } 33 | }) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /02_factory/023_abstract_factory/abstract_factory.go: -------------------------------------------------------------------------------- 1 | package factory 2 | 3 | // IRuleConfigParser IRuleConfigParser 4 | type IRuleConfigParser interface { 5 | Parse(data []byte) 6 | } 7 | 8 | // jsonRuleConfigParser jsonRuleConfigParser 9 | type jsonRuleConfigParser struct{} 10 | 11 | // Parse Parse 12 | func (j jsonRuleConfigParser) Parse(data []byte) { 13 | panic("implement me") 14 | } 15 | 16 | // ISystemConfigParser ISystemConfigParser 17 | type ISystemConfigParser interface { 18 | ParseSystem(data []byte) 19 | } 20 | 21 | // jsonSystemConfigParser jsonSystemConfigParser 22 | type jsonSystemConfigParser struct{} 23 | 24 | // Parse Parse 25 | func (j jsonSystemConfigParser) ParseSystem(data []byte) { 26 | panic("implement me") 27 | } 28 | 29 | // IConfigParserFactory 工厂方法接口 30 | type IConfigParserFactory interface { 31 | CreateRuleParser() IRuleConfigParser 32 | CreateSystemParser() ISystemConfigParser 33 | } 34 | 35 | type jsonConfigParserFactory struct{} 36 | 37 | func (j jsonConfigParserFactory) CreateRuleParser() IRuleConfigParser { 38 | return jsonRuleConfigParser{} 39 | } 40 | 41 | func (j jsonConfigParserFactory) CreateSystemParser() ISystemConfigParser { 42 | return jsonSystemConfigParser{} 43 | } 44 | 45 | type yamlConfigParserFactory struct{} 46 | 47 | func (y yamlConfigParserFactory) CreateRuleParser() IRuleConfigParser { 48 | return yamlRuleConfigParser{} 49 | } 50 | 51 | func (y yamlConfigParserFactory) CreateSystemParser() ISystemConfigParser { 52 | return yamlSystemConfigParser{} 53 | } 54 | 55 | type yamlRuleConfigParser struct{} 56 | 57 | func (y yamlRuleConfigParser) Parse(data []byte) { 58 | panic("implement me") 59 | } 60 | 61 | type yamlSystemConfigParser struct{} 62 | 63 | func (y yamlSystemConfigParser) ParseSystem(data []byte) { 64 | panic("implement me") 65 | } 66 | 67 | // NewIConfigParserFactory ... 68 | // 抽象工厂基于工厂方法,用一个简单工厂封装工厂方法 69 | // 只不过一个工厂方法可创建多个相关的类 70 | func NewIConfigParserFactory(t string) IConfigParserFactory { 71 | switch t { 72 | case "json": 73 | return jsonConfigParserFactory{} 74 | case "yaml": 75 | return yamlConfigParserFactory{} 76 | } 77 | return nil 78 | } 79 | -------------------------------------------------------------------------------- /02_factory/023_abstract_factory/abstract_factory_test.go: -------------------------------------------------------------------------------- 1 | package factory 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func Test_jsonConfigParserFactory_CreateRuleParser(t *testing.T) { 9 | tests := []struct { 10 | name string 11 | want IRuleConfigParser 12 | }{ 13 | { 14 | name: "json", 15 | want: jsonRuleConfigParser{}, 16 | }, 17 | } 18 | for _, tt := range tests { 19 | t.Run(tt.name, func(t *testing.T) { 20 | j := jsonConfigParserFactory{} 21 | if got := j.CreateRuleParser(); !reflect.DeepEqual(got, tt.want) { 22 | t.Errorf("CreateRuleParser() = %v, want %v", got, tt.want) 23 | } 24 | }) 25 | } 26 | } 27 | 28 | func Test_jsonConfigParserFactory_CreateSystemParser(t *testing.T) { 29 | tests := []struct { 30 | name string 31 | want ISystemConfigParser 32 | }{ 33 | { 34 | name: "json", 35 | want: jsonSystemConfigParser{}, 36 | }, 37 | } 38 | for _, tt := range tests { 39 | t.Run(tt.name, func(t *testing.T) { 40 | j := jsonConfigParserFactory{} 41 | if got := j.CreateSystemParser(); !reflect.DeepEqual(got, tt.want) { 42 | t.Errorf("CreateSystemParser() = %v, want %v", got, tt.want) 43 | } 44 | }) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /02_factory/024_di/di.go: -------------------------------------------------------------------------------- 1 | package di 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | // Container DI 容器 9 | type Container struct { 10 | // 假设一种类型只能有一个 provider 提供 11 | providers map[reflect.Type]provider 12 | 13 | // 缓存以生成的对象 14 | results map[reflect.Type]reflect.Value 15 | } 16 | 17 | type provider struct { 18 | value reflect.Value 19 | 20 | params []reflect.Type 21 | } 22 | 23 | // New 创建一个容器 24 | func New() *Container { 25 | return &Container{ 26 | providers: map[reflect.Type]provider{}, 27 | results: map[reflect.Type]reflect.Value{}, 28 | } 29 | } 30 | 31 | // isError 判断是否是 error 类型 32 | func isError(t reflect.Type) bool { 33 | if t.Kind() != reflect.Interface { 34 | return false 35 | } 36 | return t.Implements(reflect.TypeOf(reflect.TypeOf((*error)(nil)).Elem())) 37 | } 38 | 39 | // Provide 对象提供者,需要传入一个对象的工厂方法,后续会用于对象的创建 40 | func (c *Container) Provide(constructor interface{}) error { 41 | v := reflect.ValueOf(constructor) 42 | 43 | // 仅支持函数 provider 44 | if v.Kind() != reflect.Func { 45 | return fmt.Errorf("constructor must be a func") 46 | } 47 | 48 | vt := v.Type() 49 | 50 | // 获取参数 51 | params := make([]reflect.Type, vt.NumIn()) 52 | for i := 0; i < vt.NumIn(); i++ { 53 | params[i] = vt.In(i) 54 | } 55 | 56 | // 获取返回值 57 | results := make([]reflect.Type, vt.NumOut()) 58 | for i := 0; i < vt.NumOut(); i++ { 59 | results[i] = vt.Out(i) 60 | } 61 | 62 | provider := provider{ 63 | value: v, 64 | params: params, 65 | } 66 | 67 | // 保存不同类型的 provider 68 | for _, result := range results { 69 | // 判断返回值是不是 error 70 | if isError(result) { 71 | continue 72 | } 73 | 74 | if _, ok := c.providers[result]; ok { 75 | return fmt.Errorf("%s had a provider", result) 76 | } 77 | 78 | c.providers[result] = provider 79 | } 80 | 81 | return nil 82 | } 83 | 84 | // Invoke 函数执行入口 85 | func (c *Container) Invoke(function interface{}) error { 86 | v := reflect.ValueOf(function) 87 | 88 | // 仅支持函数 provider 89 | if v.Kind() != reflect.Func { 90 | return fmt.Errorf("constructor must be a func") 91 | } 92 | 93 | vt := v.Type() 94 | 95 | // 获取参数 96 | var err error 97 | params := make([]reflect.Value, vt.NumIn()) 98 | for i := 0; i < vt.NumIn(); i++ { 99 | params[i], err = c.buildParam(vt.In(i)) 100 | if err != nil { 101 | return err 102 | } 103 | } 104 | 105 | v.Call(params) 106 | 107 | // 获取 providers 108 | return nil 109 | } 110 | 111 | // buildParam 构建参数 112 | // 1. 从容器中获取 provider 113 | // 2. 递归获取 provider 的参数值 114 | // 3. 获取到参数之后执行函数 115 | // 4. 将结果缓存并且返回结果 116 | func (c *Container) buildParam(param reflect.Type) (val reflect.Value, err error) { 117 | if result, ok := c.results[param]; ok { 118 | return result, nil 119 | } 120 | 121 | provider, ok := c.providers[param] 122 | if !ok { 123 | return reflect.Value{}, fmt.Errorf("can not found provider: %s", param) 124 | } 125 | 126 | params := make([]reflect.Value, len(provider.params)) 127 | for i, p := range provider.params { 128 | params[i], err = c.buildParam(p) 129 | if err != nil { 130 | return reflect.Value{}, err 131 | } 132 | } 133 | 134 | results := provider.value.Call(params) 135 | for _, result := range results { 136 | // 判断是否报错 137 | if isError(result.Type()) && !result.IsNil() { 138 | return reflect.Value{}, fmt.Errorf("%+v call err: %+v", provider, result) 139 | } 140 | 141 | if !isError(result.Type()) && !result.IsNil() { 142 | c.results[result.Type()] = result 143 | } 144 | } 145 | return c.results[param], nil 146 | } 147 | -------------------------------------------------------------------------------- /02_factory/024_di/example/example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | di "github.com/mohuishou/go-design-pattern/02_factory/024_di" 7 | ) 8 | 9 | // A 依赖关系 A -> B -> C 10 | type A struct { 11 | B *B 12 | } 13 | 14 | // NewA NewA 15 | func NewA(b *B) *A { 16 | return &A{ 17 | B: b, 18 | } 19 | } 20 | 21 | // B B 22 | type B struct { 23 | C *C 24 | } 25 | 26 | // NewB NewB 27 | func NewB(c *C) *B { 28 | return &B{C: c} 29 | } 30 | 31 | // C C 32 | type C struct { 33 | Num int 34 | } 35 | 36 | // NewC NewC 37 | func NewC() *C { 38 | return &C{ 39 | Num: 1, 40 | } 41 | } 42 | 43 | func main() { 44 | container := di.New() 45 | if err := container.Provide(NewA); err != nil { 46 | panic(err) 47 | } 48 | if err := container.Provide(NewB); err != nil { 49 | panic(err) 50 | } 51 | if err := container.Provide(NewC); err != nil { 52 | panic(err) 53 | } 54 | 55 | err := container.Invoke(func(a *A) { 56 | fmt.Printf("%+v: %d", a, a.B.C.Num) 57 | }) 58 | if err != nil { 59 | panic(err) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /02_factory/readme.md: -------------------------------------------------------------------------------- 1 | # 工厂模式(Factory) 2 | 3 | 详情可以查看博客: https://lailin.xyz/post/factory.html 4 | 5 | ## 简单工厂 6 | 7 | 由于 Go 本身是没有构造函数的,一般而言我们采用 NewName 的方式创建对象/接口,当它返回的是接口的时候,其实就是简单工厂模式 8 | 9 | ## 工厂方法 10 | 11 | 当对象的创建逻辑比较复杂,不只是简单的 new 一下就可以,而是要组合其他类对象,做各种初始化操作的时候,我们推荐使用工厂方法模式,将复杂的创建逻辑拆分到多个工厂类中,让每个工厂类都不至于过于复杂 12 | 13 | ## 抽象工厂 14 | 15 | 一个工厂方法可以创建**相关联**的多个类的时候就是抽象工厂模式,这个不太常用 16 | 17 | ## DI 容器 18 | 我们这里的实现比较粗糙,但是作为一个 demo 理解 di 容器也足够了,和 dig 相比还缺少很多东西,并且有许多的问题,例如 依赖关系,一种类型如果有多个 provider 如何处理等等等等。 19 | -------------------------------------------------------------------------------- /03_builder/builder.go: -------------------------------------------------------------------------------- 1 | package builder 2 | 3 | import "fmt" 4 | 5 | const ( 6 | defaultMaxTotal = 10 7 | defaultMaxIdle = 9 8 | defaultMinIdle = 1 9 | ) 10 | 11 | // ResourcePoolConfig resource pool 12 | type ResourcePoolConfig struct { 13 | name string 14 | maxTotal int 15 | maxIdle int 16 | minIdle int 17 | } 18 | 19 | // ResourcePoolConfigBuilder 用于构建 ResourcePoolConfig 20 | type ResourcePoolConfigBuilder struct { 21 | err error 22 | name string 23 | maxTotal int 24 | maxIdle int 25 | minIdle int 26 | } 27 | 28 | // SetName SetName 29 | func (b *ResourcePoolConfigBuilder) SetName(name string) { 30 | if b.err != nil { 31 | return 32 | } 33 | if name == "" { 34 | b.err = fmt.Errorf("name can not be empty") 35 | } 36 | b.name = name 37 | } 38 | 39 | // SetMinIdle SetMinIdle 40 | func (b *ResourcePoolConfigBuilder) SetMinIdle(minIdle int) { 41 | if b.err != nil { 42 | return 43 | } 44 | if minIdle < 0 { 45 | b.err = fmt.Errorf("max tatal cannot < 0, input: %d", minIdle) 46 | } 47 | b.minIdle = minIdle 48 | } 49 | 50 | // SetMaxIdle SetMaxIdle 51 | func (b *ResourcePoolConfigBuilder) SetMaxIdle(maxIdle int) { 52 | if b.err != nil { 53 | return 54 | } 55 | if maxIdle < 0 { 56 | b.err = fmt.Errorf("max tatal cannot < 0, input: %d", maxIdle) 57 | } 58 | b.maxIdle = maxIdle 59 | } 60 | 61 | // SetMaxTotal SetMaxTotal 62 | func (b *ResourcePoolConfigBuilder) SetMaxTotal(maxTotal int) { 63 | if b.err != nil { 64 | return 65 | } 66 | if maxTotal <= 0 { 67 | b.err = fmt.Errorf("max tatal cannot <= 0, input: %d", maxTotal) 68 | } 69 | b.maxTotal = maxTotal 70 | } 71 | 72 | // Build Build 73 | func (b *ResourcePoolConfigBuilder) Build() (*ResourcePoolConfig, error) { 74 | if b.err != nil { 75 | return nil, b.err 76 | } 77 | 78 | if b.name == "" { 79 | return nil, fmt.Errorf("name can not be empty") 80 | } 81 | 82 | // 设置默认值 83 | if b.minIdle == 0 { 84 | b.minIdle = defaultMinIdle 85 | } 86 | 87 | if b.maxIdle == 0 { 88 | b.maxIdle = defaultMaxIdle 89 | } 90 | 91 | if b.maxTotal == 0 { 92 | b.maxTotal = defaultMaxTotal 93 | } 94 | 95 | if b.maxTotal < b.maxIdle { 96 | return nil, fmt.Errorf("max total(%d) cannot < max idle(%d)", b.maxTotal, b.maxIdle) 97 | } 98 | 99 | if b.minIdle > b.maxIdle { 100 | return nil, fmt.Errorf("max idle(%d) cannot < min idle(%d)", b.maxIdle, b.minIdle) 101 | } 102 | 103 | return &ResourcePoolConfig{ 104 | name: b.name, 105 | maxTotal: b.maxTotal, 106 | maxIdle: b.maxIdle, 107 | minIdle: b.minIdle, 108 | }, nil 109 | } 110 | -------------------------------------------------------------------------------- /03_builder/builder_test.go: -------------------------------------------------------------------------------- 1 | package builder 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestResourcePoolConfigBuilder_Build(t *testing.T) { 11 | tests := []struct { 12 | name string 13 | builder *ResourcePoolConfigBuilder 14 | want *ResourcePoolConfig 15 | wantErr bool 16 | }{ 17 | { 18 | name: "name empty", 19 | builder: &ResourcePoolConfigBuilder{ 20 | name: "", 21 | maxTotal: 0, 22 | }, 23 | want: nil, 24 | wantErr: true, 25 | }, 26 | { 27 | name: "maxIdle < minIdle", 28 | builder: &ResourcePoolConfigBuilder{ 29 | name: "test", 30 | maxTotal: 0, 31 | maxIdle: 10, 32 | minIdle: 20, 33 | }, 34 | want: nil, 35 | wantErr: true, 36 | }, 37 | { 38 | name: "success", 39 | builder: &ResourcePoolConfigBuilder{ 40 | name: "test", 41 | }, 42 | want: &ResourcePoolConfig{ 43 | name: "test", 44 | maxTotal: defaultMaxTotal, 45 | maxIdle: defaultMaxIdle, 46 | minIdle: defaultMinIdle, 47 | }, 48 | wantErr: false, 49 | }, 50 | } 51 | for _, tt := range tests { 52 | t.Run(tt.name, func(t *testing.T) { 53 | got, err := tt.builder.Build() 54 | require.Equalf(t, tt.wantErr, err != nil, "Build() error = %v, wantErr %v", err, tt.wantErr) 55 | assert.Equal(t, tt.want, got) 56 | }) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /03_builder/option.go: -------------------------------------------------------------------------------- 1 | package builder 2 | 3 | import "fmt" 4 | 5 | // ResourcePoolConfigOption option 6 | type ResourcePoolConfigOption struct { 7 | maxTotal int 8 | maxIdle int 9 | minIdle int 10 | } 11 | 12 | // ResourcePoolConfigOptFunc to set option 13 | type ResourcePoolConfigOptFunc func(option *ResourcePoolConfigOption) 14 | 15 | // NewResourcePoolConfig NewResourcePoolConfig 16 | func NewResourcePoolConfig(name string, opts ...ResourcePoolConfigOptFunc) (*ResourcePoolConfig, error) { 17 | if name == "" { 18 | return nil, fmt.Errorf("name can not be empty") 19 | } 20 | 21 | option := &ResourcePoolConfigOption{ 22 | maxTotal: 10, 23 | maxIdle: 9, 24 | minIdle: 1, 25 | } 26 | 27 | for _, opt := range opts { 28 | opt(option) 29 | } 30 | 31 | if option.maxTotal < 0 || option.maxIdle < 0 || option.minIdle < 0 { 32 | return nil, fmt.Errorf("args err, option: %v", option) 33 | } 34 | 35 | if option.maxTotal < option.maxIdle || option.minIdle > option.maxIdle { 36 | return nil, fmt.Errorf("args err, option: %v", option) 37 | } 38 | 39 | return &ResourcePoolConfig{ 40 | name: name, 41 | maxTotal: option.maxTotal, 42 | maxIdle: option.maxIdle, 43 | minIdle: option.minIdle, 44 | }, nil 45 | } 46 | -------------------------------------------------------------------------------- /03_builder/option_test.go: -------------------------------------------------------------------------------- 1 | package builder 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestNewResourcePoolConfig(t *testing.T) { 11 | type args struct { 12 | name string 13 | opts []ResourcePoolConfigOptFunc 14 | } 15 | tests := []struct { 16 | name string 17 | args args 18 | want *ResourcePoolConfig 19 | wantErr bool 20 | }{ 21 | { 22 | name: "name empty", 23 | args: args{ 24 | name: "", 25 | }, 26 | want: nil, 27 | wantErr: true, 28 | }, 29 | { 30 | name: "success", 31 | args: args{ 32 | name: "test", 33 | opts: []ResourcePoolConfigOptFunc{ 34 | func(option *ResourcePoolConfigOption) { 35 | option.minIdle = 2 36 | }, 37 | }, 38 | }, 39 | want: &ResourcePoolConfig{ 40 | name: "test", 41 | maxTotal: 10, 42 | maxIdle: 9, 43 | minIdle: 2, 44 | }, 45 | wantErr: false, 46 | }, 47 | } 48 | for _, tt := range tests { 49 | t.Run(tt.name, func(t *testing.T) { 50 | got, err := NewResourcePoolConfig(tt.args.name, tt.args.opts...) 51 | require.Equalf(t, tt.wantErr, err != nil, "error = %v, wantErr %v", err, tt.wantErr) 52 | assert.Equal(t, tt.want, got) 53 | }) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /04_prototype/prototype.go: -------------------------------------------------------------------------------- 1 | package prototype 2 | 3 | import ( 4 | "encoding/json" 5 | "time" 6 | ) 7 | 8 | // Keyword 搜索关键字 9 | type Keyword struct { 10 | word string 11 | visit int 12 | UpdatedAt *time.Time 13 | } 14 | 15 | // Clone 这里使用序列化与反序列化的方式深拷贝 16 | func (k *Keyword) Clone() *Keyword { 17 | var newKeyword Keyword 18 | b, _ := json.Marshal(k) 19 | json.Unmarshal(b, &newKeyword) 20 | return &newKeyword 21 | } 22 | 23 | // Keywords 关键字 map 24 | type Keywords map[string]*Keyword 25 | 26 | // Clone 复制一个新的 keywords 27 | // updatedWords: 需要更新的关键词列表,由于从数据库中获取数据常常是数组的方式 28 | func (words Keywords) Clone(updatedWords []*Keyword) Keywords { 29 | newKeywords := Keywords{} 30 | 31 | for k, v := range words { 32 | // 这里是浅拷贝,直接拷贝了地址 33 | newKeywords[k] = v 34 | } 35 | 36 | // 替换掉需要更新的字段,这里用的是深拷贝 37 | for _, word := range updatedWords { 38 | newKeywords[word.word] = word.Clone() 39 | } 40 | 41 | return newKeywords 42 | } 43 | -------------------------------------------------------------------------------- /04_prototype/prototype_test.go: -------------------------------------------------------------------------------- 1 | package prototype 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestKeywords_Clone(t *testing.T) { 11 | updateAt, _ := time.Parse("2006", "2020") 12 | words := Keywords{ 13 | "testA": &Keyword{ 14 | word: "testA", 15 | visit: 1, 16 | UpdatedAt: &updateAt, 17 | }, 18 | "testB": &Keyword{ 19 | word: "testB", 20 | visit: 2, 21 | UpdatedAt: &updateAt, 22 | }, 23 | "testC": &Keyword{ 24 | word: "testC", 25 | visit: 3, 26 | UpdatedAt: &updateAt, 27 | }, 28 | } 29 | 30 | now := time.Now() 31 | updatedWords := []*Keyword{ 32 | { 33 | word: "testB", 34 | visit: 10, 35 | UpdatedAt: &now, 36 | }, 37 | } 38 | 39 | got := words.Clone(updatedWords) 40 | 41 | assert.Equal(t, words["testA"], got["testA"]) 42 | assert.NotEqual(t, words["testB"], got["testB"]) 43 | assert.NotEqual(t, updatedWords[0], got["testB"]) 44 | assert.Equal(t, words["testC"], got["testC"]) 45 | } 46 | -------------------------------------------------------------------------------- /05_proxy/dynamic_proxy.go: -------------------------------------------------------------------------------- 1 | package proxy 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "go/ast" 7 | "go/format" 8 | "go/parser" 9 | "go/token" 10 | "strings" 11 | "text/template" 12 | ) 13 | 14 | func generate(file string) (string, error) { 15 | fset := token.NewFileSet() // positions are relative to fset 16 | f, err := parser.ParseFile(fset, file, nil, parser.ParseComments) 17 | if err != nil { 18 | return "", err 19 | } 20 | 21 | // 获取代理需要的数据 22 | data := proxyData{ 23 | Package: f.Name.Name, 24 | } 25 | 26 | // 构建注释和 node 的关系 27 | cmap := ast.NewCommentMap(fset, f, f.Comments) 28 | for node, group := range cmap { 29 | // 从注释 @proxy 接口名,获取接口名称 30 | name := getProxyInterfaceName(group) 31 | if name == "" { 32 | continue 33 | } 34 | 35 | // 获取代理的类名 36 | data.ProxyStructName = node.(*ast.GenDecl).Specs[0].(*ast.TypeSpec).Name.Name 37 | 38 | // 从文件中查找接口 39 | obj := f.Scope.Lookup(name) 40 | 41 | // 类型转换,注意: 这里没有对断言进行判断,可能会导致 panic 42 | t := obj.Decl.(*ast.TypeSpec).Type.(*ast.InterfaceType) 43 | 44 | for _, field := range t.Methods.List { 45 | fc := field.Type.(*ast.FuncType) 46 | 47 | // 代理的方法 48 | method := &proxyMethod{ 49 | Name: field.Names[0].Name, 50 | } 51 | 52 | // 获取方法的参数和返回值 53 | method.Params, method.ParamNames = getParamsOrResults(fc.Params) 54 | method.Results, method.ResultNames = getParamsOrResults(fc.Results) 55 | 56 | data.Methods = append(data.Methods, method) 57 | } 58 | } 59 | 60 | // 生成文件 61 | tpl, err := template.New("").Parse(proxyTpl) 62 | if err != nil { 63 | return "", err 64 | } 65 | 66 | buf := &bytes.Buffer{} 67 | if err := tpl.Execute(buf, data); err != nil { 68 | return "", err 69 | } 70 | 71 | // 使用 go fmt 对生成的代码进行格式化 72 | src, err := format.Source(buf.Bytes()) 73 | if err != nil { 74 | return "", err 75 | } 76 | 77 | return string(src), nil 78 | } 79 | 80 | // getParamsOrResults 获取参数或者是返回值 81 | // 返回带类型的参数,以及不带类型的参数,以逗号间隔 82 | func getParamsOrResults(fields *ast.FieldList) (string, string) { 83 | var ( 84 | params []string 85 | paramNames []string 86 | ) 87 | 88 | for i, param := range fields.List { 89 | // 循环获取所有的参数名 90 | var names []string 91 | for _, name := range param.Names { 92 | names = append(names, name.Name) 93 | } 94 | 95 | if len(names) == 0 { 96 | names = append(names, fmt.Sprintf("r%d", i)) 97 | } 98 | 99 | paramNames = append(paramNames, names...) 100 | 101 | // 参数名加参数类型组成完整的参数 102 | param := fmt.Sprintf("%s %s", 103 | strings.Join(names, ","), 104 | param.Type.(*ast.Ident).Name, 105 | ) 106 | params = append(params, strings.TrimSpace(param)) 107 | } 108 | 109 | return strings.Join(params, ","), strings.Join(paramNames, ",") 110 | } 111 | 112 | func getProxyInterfaceName(groups []*ast.CommentGroup) string { 113 | for _, commentGroup := range groups { 114 | for _, comment := range commentGroup.List { 115 | if strings.Contains(comment.Text, "@proxy") { 116 | interfaceName := strings.TrimLeft(comment.Text, "// @proxy ") 117 | return strings.TrimSpace(interfaceName) 118 | } 119 | } 120 | } 121 | return "" 122 | } 123 | 124 | // 生成代理类的文件模板 125 | const proxyTpl = ` 126 | package {{.Package}} 127 | 128 | type {{ .ProxyStructName }}Proxy struct { 129 | child *{{ .ProxyStructName }} 130 | } 131 | 132 | func New{{ .ProxyStructName }}Proxy(child *{{ .ProxyStructName }}) *{{ .ProxyStructName }}Proxy { 133 | return &{{ .ProxyStructName }}Proxy{child: child} 134 | } 135 | 136 | {{ range .Methods }} 137 | func (p *{{$.ProxyStructName}}Proxy) {{ .Name }} ({{ .Params }}) ({{ .Results }}) { 138 | // before 这里可能会有一些统计的逻辑 139 | start := time.Now() 140 | 141 | {{ .ResultNames }} = p.child.{{ .Name }}({{ .ParamNames }}) 142 | 143 | // after 这里可能也有一些监控统计的逻辑 144 | log.Printf("user login cost time: %s", time.Now().Sub(start)) 145 | 146 | return {{ .ResultNames }} 147 | } 148 | {{ end }} 149 | ` 150 | 151 | type proxyData struct { 152 | // 包名 153 | Package string 154 | // 需要代理的类名 155 | ProxyStructName string 156 | // 需要代理的方法 157 | Methods []*proxyMethod 158 | } 159 | 160 | // proxyMethod 代理的方法 161 | type proxyMethod struct { 162 | // 方法名 163 | Name string 164 | // 参数,含参数类型 165 | Params string 166 | // 参数名 167 | ParamNames string 168 | // 返回值 169 | Results string 170 | // 返回值名 171 | ResultNames string 172 | } 173 | -------------------------------------------------------------------------------- /05_proxy/dynamic_proxy_test.go: -------------------------------------------------------------------------------- 1 | package proxy 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func Test_generate(t *testing.T) { 11 | want := `package proxy 12 | 13 | type UserProxy struct { 14 | child *User 15 | } 16 | 17 | func NewUserProxy(child *User) *UserProxy { 18 | return &UserProxy{child: child} 19 | } 20 | 21 | func (p *UserProxy) Login(username, password string) (r0 error) { 22 | // before 这里可能会有一些统计的逻辑 23 | start := time.Now() 24 | 25 | r0 = p.child.Login(username, password) 26 | 27 | // after 这里可能也有一些监控统计的逻辑 28 | log.Printf("user login cost time: %s", time.Now().Sub(start)) 29 | 30 | return r0 31 | } 32 | ` 33 | got, err := generate("./static_proxy.go") 34 | require.Nil(t, err) 35 | assert.Equal(t, want, got) 36 | } 37 | -------------------------------------------------------------------------------- /05_proxy/static_proxy.go: -------------------------------------------------------------------------------- 1 | package proxy 2 | 3 | import ( 4 | "log" 5 | "time" 6 | ) 7 | 8 | // IUser IUser 9 | type IUser interface { 10 | Login(username, password string) error 11 | } 12 | 13 | // User 用户 14 | // @proxy IUser 15 | type User struct { 16 | } 17 | 18 | // Login 用户登录 19 | func (u *User) Login(username, password string) error { 20 | // 不实现细节 21 | return nil 22 | } 23 | 24 | // UserProxy 代理类 25 | type UserProxy struct { 26 | user *User 27 | } 28 | 29 | // NewUserProxy NewUserProxy 30 | func NewUserProxy(user *User) *UserProxy { 31 | return &UserProxy{ 32 | user: user, 33 | } 34 | } 35 | 36 | // Login 登录,和 user 实现相同的接口 37 | func (p *UserProxy) Login(username, password string) error { 38 | // before 这里可能会有一些统计的逻辑 39 | start := time.Now() 40 | 41 | // 这里是原有的业务逻辑 42 | if err := p.user.Login(username, password); err != nil { 43 | return err 44 | } 45 | 46 | // after 这里可能也有一些监控统计的逻辑 47 | log.Printf("user login cost time: %s", time.Now().Sub(start)) 48 | 49 | return nil 50 | } 51 | -------------------------------------------------------------------------------- /05_proxy/static_proxy_test.go: -------------------------------------------------------------------------------- 1 | package proxy 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestUserProxy_Login(t *testing.T) { 10 | proxy := NewUserProxy(&User{}) 11 | 12 | err := proxy.Login("test", "password") 13 | 14 | require.Nil(t, err) 15 | } 16 | -------------------------------------------------------------------------------- /06_bridge/bridge.go: -------------------------------------------------------------------------------- 1 | package bridge 2 | 3 | // IMsgSender IMsgSender 4 | type IMsgSender interface { 5 | Send(msg string) error 6 | } 7 | 8 | // EmailMsgSender 发送邮件 9 | // 可能还有 电话、短信等各种实现 10 | type EmailMsgSender struct { 11 | emails []string 12 | } 13 | 14 | // NewEmailMsgSender NewEmailMsgSender 15 | func NewEmailMsgSender(emails []string) *EmailMsgSender { 16 | return &EmailMsgSender{emails: emails} 17 | } 18 | 19 | // Send Send 20 | func (s *EmailMsgSender) Send(msg string) error { 21 | // 这里去发送消息 22 | return nil 23 | } 24 | 25 | // INotification 通知接口 26 | type INotification interface { 27 | Notify(msg string) error 28 | } 29 | 30 | // ErrorNotification 错误通知 31 | // 后面可能还有 warning 各种级别 32 | type ErrorNotification struct { 33 | sender IMsgSender 34 | } 35 | 36 | // NewErrorNotification NewErrorNotification 37 | func NewErrorNotification(sender IMsgSender) *ErrorNotification { 38 | return &ErrorNotification{sender: sender} 39 | } 40 | 41 | // Notify 发送通知 42 | func (n *ErrorNotification) Notify(msg string) error { 43 | return n.sender.Send(msg) 44 | } 45 | -------------------------------------------------------------------------------- /06_bridge/bridge_test.go: -------------------------------------------------------------------------------- 1 | package bridge 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestErrorNotification_Notify(t *testing.T) { 10 | sender := NewEmailMsgSender([]string{"test@test.com"}) 11 | n := NewErrorNotification(sender) 12 | err := n.Notify("test msg") 13 | 14 | assert.Nil(t, err) 15 | } 16 | -------------------------------------------------------------------------------- /07_decorator/decorator.go: -------------------------------------------------------------------------------- 1 | package decorator 2 | 3 | // IDraw IDraw 4 | type IDraw interface { 5 | Draw() string 6 | } 7 | 8 | // Square 正方形 9 | type Square struct{} 10 | 11 | // Draw Draw 12 | func (s Square) Draw() string { 13 | return "this is a square" 14 | } 15 | 16 | // ColorSquare 有颜色的正方形 17 | type ColorSquare struct { 18 | square IDraw 19 | color string 20 | } 21 | 22 | // NewColorSquare NewColorSquare 23 | func NewColorSquare(square IDraw, color string) ColorSquare { 24 | return ColorSquare{color: color, square: square} 25 | } 26 | 27 | // Draw Draw 28 | func (c ColorSquare) Draw() string { 29 | return c.square.Draw() + ", color is " + c.color 30 | } 31 | -------------------------------------------------------------------------------- /07_decorator/decorator_test.go: -------------------------------------------------------------------------------- 1 | package decorator 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestColorSquare_Draw(t *testing.T) { 10 | sq := Square{} 11 | csq := NewColorSquare(sq, "red") 12 | got := csq.Draw() 13 | assert.Equal(t, "this is a square, color is red", got) 14 | } 15 | -------------------------------------------------------------------------------- /08_adapter/adapter.go: -------------------------------------------------------------------------------- 1 | package adapter 2 | 3 | import "fmt" 4 | 5 | // ICreateServer 创建云主机 6 | type ICreateServer interface { 7 | CreateServer(cpu, mem float64) error 8 | } 9 | 10 | // AWSClient aws sdk 11 | type AWSClient struct{} 12 | 13 | // RunInstance 启动实例 14 | func (c *AWSClient) RunInstance(cpu, mem float64) error { 15 | fmt.Printf("aws client run success, cpu: %f, mem: %f", cpu, mem) 16 | return nil 17 | } 18 | 19 | // AwsClientAdapter 适配器 20 | type AwsClientAdapter struct { 21 | Client AWSClient 22 | } 23 | 24 | // CreateServer 启动实例 25 | func (a *AwsClientAdapter) CreateServer(cpu, mem float64) error { 26 | a.Client.RunInstance(cpu, mem) 27 | return nil 28 | } 29 | 30 | // AliyunClient aliyun sdk 31 | type AliyunClient struct{} 32 | 33 | // CreateServer 启动实例 34 | func (c *AliyunClient) CreateServer(cpu, mem int) error { 35 | fmt.Printf("aws client run success, cpu: %d, mem: %d", cpu, mem) 36 | return nil 37 | } 38 | 39 | // AliyunClientAdapter 适配器 40 | type AliyunClientAdapter struct { 41 | Client AliyunClient 42 | } 43 | 44 | // CreateServer 启动实例 45 | func (a *AliyunClientAdapter) CreateServer(cpu, mem float64) error { 46 | a.Client.CreateServer(int(cpu), int(mem)) 47 | return nil 48 | } 49 | -------------------------------------------------------------------------------- /08_adapter/adapter_test.go: -------------------------------------------------------------------------------- 1 | package adapter 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestAliyunClientAdapter_CreateServer(t *testing.T) { 8 | // 确保 adapter 实现了目标接口 9 | var a ICreateServer = &AliyunClientAdapter{ 10 | Client: AliyunClient{}, 11 | } 12 | 13 | a.CreateServer(1.0, 2.0) 14 | } 15 | 16 | func TestAwsClientAdapter_CreateServer(t *testing.T) { 17 | // 确保 adapter 实现了目标接口 18 | var a ICreateServer = &AwsClientAdapter{ 19 | Client: AWSClient{}, 20 | } 21 | 22 | a.CreateServer(1.0, 2.0) 23 | } 24 | -------------------------------------------------------------------------------- /08_adapter/readme.md: -------------------------------------------------------------------------------- 1 | ## 笔记 2 | 3 | ![](https://lailin.xyz/images/1600352298306-7105ca29-089d-4f54-bb65-82d3c0c3b31c.png) 4 | 5 | ## 代码实现 6 | 7 | 假设我现在有一个运维系统,需要分别调用阿里云和 AWS 的 SDK 创建主机,两个 SDK 提供的创建主机的接口不一致,此时就可以通过适配器模式,将两个接口统一。 8 | **PS:AWS 和 阿里云的接口纯属虚构,没有直接用原始的 SDK,只是举个例子** -------------------------------------------------------------------------------- /09_facade/facade.go: -------------------------------------------------------------------------------- 1 | package facade 2 | 3 | // IUser 用户接口 4 | type IUser interface { 5 | Login(phone int, code int) (*User, error) 6 | Register(phone int, code int) (*User, error) 7 | } 8 | 9 | // IUserFacade 门面模式 10 | type IUserFacade interface { 11 | LoginOrRegister(phone int, code int) (*User, error) 12 | } 13 | 14 | // User 用户 15 | type User struct { 16 | Name string 17 | } 18 | 19 | func newUser(name string) IUser { 20 | return User{Name: name} 21 | } 22 | 23 | // Login 登录 24 | func (u User) Login(phone int, code int) (*User, error) { 25 | // 校验操作 ... 26 | return &User{Name: "test login"}, nil 27 | } 28 | 29 | // Register 注册 30 | func (u User) Register(phone int, code int) (*User, error) { 31 | // 校验操作 ... 32 | // 创建用户 33 | return &User{Name: "test register"}, nil 34 | } 35 | 36 | // UserService facade struct 37 | type UserService struct { 38 | User IUser 39 | } 40 | 41 | func NewUserService(name string) UserService { 42 | return UserService{ 43 | User: newUser(name), 44 | } 45 | } 46 | 47 | // LoginOrRegister 登录或注册 48 | func (u UserService) LoginOrRegister(phone int, code int) (*User, error) { 49 | user, err := u.User.Login(phone, code) 50 | if err != nil { 51 | return nil, err 52 | } 53 | 54 | if user != nil { 55 | return user, nil 56 | } 57 | 58 | return u.User.Register(phone, code) 59 | } 60 | -------------------------------------------------------------------------------- /09_facade/facade_test.go: -------------------------------------------------------------------------------- 1 | package facade 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestUserService_Login(t *testing.T) { 9 | service := NewUserService("lily") 10 | user, err := service.User.Login(13001010101, 1234) 11 | assert.NoError(t, err) 12 | assert.Equal(t, &User{Name: "test login"}, user) 13 | } 14 | 15 | func TestUserService_LoginOrRegister(t *testing.T) { 16 | service := NewUserService("lily") 17 | user, err := service.LoginOrRegister(13001010101, 1234) 18 | assert.NoError(t, err) 19 | assert.Equal(t, &User{Name: "test login"}, user) 20 | } 21 | 22 | func TestUserService_Register(t *testing.T) { 23 | service := NewUserService("lily") 24 | user, err := service.User.Register(13001010101, 1234) 25 | assert.NoError(t, err) 26 | assert.Equal(t, &User{Name: "test register"}, user) 27 | } 28 | -------------------------------------------------------------------------------- /09_facade/readme.md: -------------------------------------------------------------------------------- 1 | ## 笔记 2 | 3 | ![](https://lailin.xyz/images/1600423555200-19b2f597-a01f-4ad3-ae25-c456df81cafa.png) 4 | 5 | ## 代码实现 6 | 7 | 假设现在我有一个网站,以前有登录和注册的流程,登录的时候调用用户的查询接口,注册时调用用户的创建接口。为了简化用户的使用流程,我们现在提供直接验证码登录/注册的功能,如果该手机号已注册那么我们就走登录流程,如果该手机号未注册,那么我们就创建一个新的用户。 8 | -------------------------------------------------------------------------------- /10_composite/composite.go: -------------------------------------------------------------------------------- 1 | package composite 2 | 3 | // IOrganization 组织接口,都实现统计人数的功能 4 | type IOrganization interface { 5 | Count() int 6 | } 7 | 8 | // Employee 员工 9 | type Employee struct { 10 | Name string 11 | } 12 | 13 | // Count 人数统计 14 | func (Employee) Count() int { 15 | return 1 16 | } 17 | 18 | // Department 部门 19 | type Department struct { 20 | Name string 21 | 22 | SubOrganizations []IOrganization 23 | } 24 | 25 | // Count 人数统计 26 | func (d Department) Count() int { 27 | c := 0 28 | for _, org := range d.SubOrganizations { 29 | c += org.Count() 30 | } 31 | return c 32 | } 33 | 34 | // AddSub 添加子节点 35 | func (d *Department) AddSub(org IOrganization) { 36 | d.SubOrganizations = append(d.SubOrganizations, org) 37 | } 38 | 39 | // NewOrganization 构建组织架构 demo 40 | func NewOrganization() IOrganization { 41 | root := &Department{Name: "root"} 42 | for i := 0; i < 10; i++ { 43 | root.AddSub(&Employee{}) 44 | root.AddSub(&Department{Name: "sub", SubOrganizations: []IOrganization{&Employee{}}}) 45 | } 46 | return root 47 | } 48 | -------------------------------------------------------------------------------- /10_composite/composite_test.go: -------------------------------------------------------------------------------- 1 | package composite 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestNewOrganization(t *testing.T) { 10 | got := NewOrganization().Count() 11 | assert.Equal(t, 20, got) 12 | } 13 | -------------------------------------------------------------------------------- /11_flyweight/flyweight.go: -------------------------------------------------------------------------------- 1 | package flyweight 2 | 3 | var units = map[int]*ChessPieceUnit{ 4 | 1: { 5 | ID: 1, 6 | Name: "車", 7 | Color: "red", 8 | }, 9 | 2: { 10 | ID: 2, 11 | Name: "炮", 12 | Color: "red", 13 | }, 14 | // ... 其他棋子 15 | } 16 | 17 | // ChessPieceUnit 棋子享元 18 | type ChessPieceUnit struct { 19 | ID uint 20 | Name string 21 | Color string 22 | } 23 | 24 | // NewChessPieceUnit 工厂 25 | func NewChessPieceUnit(id int) *ChessPieceUnit { 26 | return units[id] 27 | } 28 | 29 | // ChessPiece 棋子 30 | type ChessPiece struct { 31 | Unit *ChessPieceUnit 32 | X int 33 | Y int 34 | } 35 | 36 | // ChessBoard 棋局 37 | type ChessBoard struct { 38 | chessPieces map[int]*ChessPiece 39 | } 40 | 41 | // NewChessBoard 初始化棋盘 42 | func NewChessBoard() *ChessBoard { 43 | board := &ChessBoard{chessPieces: map[int]*ChessPiece{}} 44 | for id := range units { 45 | board.chessPieces[id] = &ChessPiece{ 46 | Unit: NewChessPieceUnit(id), 47 | X: 0, 48 | Y: 0, 49 | } 50 | } 51 | return board 52 | } 53 | 54 | // Move 移动棋子 55 | func (c *ChessBoard) Move(id, x, y int) { 56 | c.chessPieces[id].X = x 57 | c.chessPieces[id].Y = y 58 | } 59 | -------------------------------------------------------------------------------- /11_flyweight/flyweight_test.go: -------------------------------------------------------------------------------- 1 | package flyweight 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestNewChessBoard(t *testing.T) { 10 | board1 := NewChessBoard() 11 | board1.Move(1, 1, 2) 12 | board2 := NewChessBoard() 13 | board2.Move(2, 2, 3) 14 | 15 | assert.Equal(t, board1.chessPieces[1].Unit, board2.chessPieces[1].Unit) 16 | assert.Equal(t, board1.chessPieces[2].Unit, board2.chessPieces[2].Unit) 17 | } 18 | -------------------------------------------------------------------------------- /12_observer/121_observer/observer.go: -------------------------------------------------------------------------------- 1 | package observer 2 | 3 | import "fmt" 4 | 5 | // ISubject subject 6 | type ISubject interface { 7 | Register(observer IObserver) 8 | Remove(observer IObserver) 9 | Notify(msg string) 10 | } 11 | 12 | // IObserver 观察者 13 | type IObserver interface { 14 | Update(msg string) 15 | } 16 | 17 | // Subject Subject 18 | type Subject struct { 19 | observers []IObserver 20 | } 21 | 22 | // Register 注册 23 | func (sub *Subject) Register(observer IObserver) { 24 | sub.observers = append(sub.observers, observer) 25 | } 26 | 27 | // Remove 移除观察者 28 | func (sub *Subject) Remove(observer IObserver) { 29 | for i, ob := range sub.observers { 30 | if ob == observer { 31 | sub.observers = append(sub.observers[:i], sub.observers[i+1:]...) 32 | } 33 | } 34 | } 35 | 36 | // Notify 通知 37 | func (sub *Subject) Notify(msg string) { 38 | for _, o := range sub.observers { 39 | o.Update(msg) 40 | } 41 | } 42 | 43 | // Observer1 Observer1 44 | type Observer1 struct{} 45 | 46 | // Update 实现观察者接口 47 | func (Observer1) Update(msg string) { 48 | fmt.Printf("Observer1: %s", msg) 49 | } 50 | 51 | // Observer2 Observer2 52 | type Observer2 struct{} 53 | 54 | // Update 实现观察者接口 55 | func (Observer2) Update(msg string) { 56 | fmt.Printf("Observer2: %s", msg) 57 | } 58 | -------------------------------------------------------------------------------- /12_observer/121_observer/observer_test.go: -------------------------------------------------------------------------------- 1 | package observer 2 | 3 | import "testing" 4 | 5 | func TestSubject_Notify(t *testing.T) { 6 | sub := &Subject{} 7 | sub.Register(&Observer1{}) 8 | sub.Register(&Observer2{}) 9 | sub.Notify("hi") 10 | } 11 | -------------------------------------------------------------------------------- /12_observer/122_eventbus/eventbus.go: -------------------------------------------------------------------------------- 1 | package eventbus 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "sync" 7 | ) 8 | 9 | // Bus Bus 10 | type Bus interface { 11 | Subscribe(topic string, handler interface{}) error 12 | Publish(topic string, args ...interface{}) 13 | } 14 | 15 | // AsyncEventBus 异步事件总线 16 | type AsyncEventBus struct { 17 | handlers map[string][]reflect.Value 18 | lock sync.Mutex 19 | } 20 | 21 | // NewAsyncEventBus new 22 | func NewAsyncEventBus() *AsyncEventBus { 23 | return &AsyncEventBus{ 24 | handlers: map[string][]reflect.Value{}, 25 | lock: sync.Mutex{}, 26 | } 27 | } 28 | 29 | // Subscribe 订阅 30 | func (bus *AsyncEventBus) Subscribe(topic string, f interface{}) error { 31 | bus.lock.Lock() 32 | defer bus.lock.Unlock() 33 | 34 | v := reflect.ValueOf(f) 35 | if v.Type().Kind() != reflect.Func { 36 | return fmt.Errorf("handler is not a function") 37 | } 38 | 39 | handler, ok := bus.handlers[topic] 40 | if !ok { 41 | handler = []reflect.Value{} 42 | } 43 | handler = append(handler, v) 44 | bus.handlers[topic] = handler 45 | 46 | return nil 47 | } 48 | 49 | // Publish 发布 50 | // 这里异步执行,并且不会等待返回结果 51 | func (bus *AsyncEventBus) Publish(topic string, args ...interface{}) { 52 | handlers, ok := bus.handlers[topic] 53 | if !ok { 54 | fmt.Println("not found handlers in topic:", topic) 55 | return 56 | } 57 | 58 | params := make([]reflect.Value, len(args)) 59 | for i, arg := range args { 60 | params[i] = reflect.ValueOf(arg) 61 | } 62 | 63 | for i := range handlers { 64 | go handlers[i].Call(params) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /12_observer/122_eventbus/eventbus_test.go: -------------------------------------------------------------------------------- 1 | package eventbus 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func sub1(msg1, msg2 string) { 10 | time.Sleep(1 * time.Microsecond) 11 | fmt.Printf("sub1, %s %s\n", msg1, msg2) 12 | } 13 | 14 | func sub2(msg1, msg2 string) { 15 | fmt.Printf("sub2, %s %s\n", msg1, msg2) 16 | } 17 | func TestAsyncEventBus_Publish(t *testing.T) { 18 | bus := NewAsyncEventBus() 19 | bus.Subscribe("topic:1", sub1) 20 | bus.Subscribe("topic:1", sub2) 21 | bus.Publish("topic:1", "test1", "test2") 22 | bus.Publish("topic:1", "testA", "testB") 23 | time.Sleep(1 * time.Second) 24 | } 25 | -------------------------------------------------------------------------------- /13_template/template.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import "fmt" 4 | 5 | // ISMS ISMS 6 | type ISMS interface { 7 | send(content string, phone int) error 8 | } 9 | 10 | // SMS 短信发送基类 11 | type sms struct { 12 | ISMS 13 | } 14 | 15 | // Valid 校验短信字数 16 | func (s *sms) Valid(content string) error { 17 | if len(content) > 63 { 18 | return fmt.Errorf("content is too long") 19 | } 20 | return nil 21 | } 22 | 23 | // Send 发送短信 24 | func (s *sms) Send(content string, phone int) error { 25 | if err := s.Valid(content); err != nil { 26 | return err 27 | } 28 | 29 | // 调用子类的方法发送短信 30 | return s.send(content, phone) 31 | } 32 | 33 | // TelecomSms 走电信通道 34 | type TelecomSms struct { 35 | *sms 36 | } 37 | 38 | // NewTelecomSms NewTelecomSms 39 | func NewTelecomSms() *TelecomSms { 40 | tel := &TelecomSms{} 41 | // 这里有点绕,是因为 go 没有继承,用嵌套结构体的方法进行模拟 42 | // 这里将子类作为接口嵌入父类,就可以让父类的模板方法 Send 调用到子类的函数 43 | // 实际使用中,我们并不会这么写,都是采用组合+接口的方式完成类似的功能 44 | tel.sms = &sms{ISMS: tel} 45 | return tel 46 | } 47 | 48 | func (tel *TelecomSms) send(content string, phone int) error { 49 | fmt.Println("send by telecom success") 50 | return nil 51 | } 52 | -------------------------------------------------------------------------------- /13_template/template_test.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func Test_sms_Send(t *testing.T) { 10 | tel := NewTelecomSms() 11 | err := tel.Send("test", 1239999) 12 | assert.NoError(t, err) 13 | } 14 | -------------------------------------------------------------------------------- /14_strategy/strategy.go: -------------------------------------------------------------------------------- 1 | package strategy 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "os" 7 | ) 8 | 9 | // StorageStrategy 存储策略 10 | type StorageStrategy interface { 11 | Save(name string, data []byte) error 12 | } 13 | 14 | var strategys = map[string]StorageStrategy{ 15 | "file": &fileStorage{}, 16 | "encrypt_file": &encryptFileStorage{}, 17 | } 18 | 19 | // NewStorageStrategy NewStorageStrategy 20 | func NewStorageStrategy(t string) (StorageStrategy, error) { 21 | s, ok := strategys[t] 22 | if !ok { 23 | return nil, fmt.Errorf("not found StorageStrategy: %s", t) 24 | } 25 | 26 | return s, nil 27 | } 28 | 29 | // FileStorage 保存到文件 30 | type fileStorage struct{} 31 | 32 | // Save Save 33 | func (s *fileStorage) Save(name string, data []byte) error { 34 | return ioutil.WriteFile(name, data, os.ModeAppend) 35 | } 36 | 37 | // encryptFileStorage 加密保存到文件 38 | type encryptFileStorage struct{} 39 | 40 | // Save Save 41 | func (s *encryptFileStorage) Save(name string, data []byte) error { 42 | // 加密 43 | data, err := encrypt(data) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | return ioutil.WriteFile(name, data, os.ModeAppend) 49 | } 50 | 51 | func encrypt(data []byte) ([]byte, error) { 52 | // 这里实现加密算法 53 | return data, nil 54 | } 55 | -------------------------------------------------------------------------------- /14_strategy/strategy_test.go: -------------------------------------------------------------------------------- 1 | package strategy 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func Test_demo(t *testing.T) { 11 | // 假设这里获取数据,以及数据是否敏感 12 | data, sensitive := getData() 13 | strategyType := "file" 14 | if sensitive { 15 | strategyType = "encrypt_file" 16 | } 17 | 18 | storage, err := NewStorageStrategy(strategyType) 19 | assert.NoError(t, err) 20 | 21 | dir, err := os.MkdirTemp("", "strategy") 22 | assert.NoError(t, err) 23 | 24 | assert.NoError(t, storage.Save(dir+"/test.txt", data)) 25 | } 26 | 27 | // getData 获取数据的方法 28 | // 返回数据,以及数据是否敏感 29 | func getData() ([]byte, bool) { 30 | return []byte("test data"), false 31 | } 32 | -------------------------------------------------------------------------------- /15_chain/chain.go: -------------------------------------------------------------------------------- 1 | // Package chain 职责链模式 2 | // 🌰 假设我们现在有个校园论坛,由于社区规章制度、广告、法律法规的原因需要对用户的发言进行敏感词过滤 3 | // 如果被判定为敏感词,那么这篇帖子将会被封禁 4 | package chain 5 | 6 | // SensitiveWordFilter 敏感词过滤器,判定是否是敏感词 7 | type SensitiveWordFilter interface { 8 | Filter(content string) bool 9 | } 10 | 11 | // SensitiveWordFilterChain 职责链 12 | type SensitiveWordFilterChain struct { 13 | filters []SensitiveWordFilter 14 | } 15 | 16 | // AddFilter 添加一个过滤器 17 | func (c *SensitiveWordFilterChain) AddFilter(filter SensitiveWordFilter) { 18 | c.filters = append(c.filters, filter) 19 | } 20 | 21 | // Filter 执行过滤 22 | func (c *SensitiveWordFilterChain) Filter(content string) bool { 23 | for _, filter := range c.filters { 24 | // 如果发现敏感直接返回结果 25 | if filter.Filter(content) { 26 | return true 27 | } 28 | } 29 | return false 30 | } 31 | 32 | // AdSensitiveWordFilter 广告 33 | type AdSensitiveWordFilter struct{} 34 | 35 | // Filter 实现过滤算法 36 | func (f *AdSensitiveWordFilter) Filter(content string) bool { 37 | // TODO: 实现算法 38 | return false 39 | } 40 | 41 | // PoliticalWordFilter 政治敏感 42 | type PoliticalWordFilter struct{} 43 | 44 | // Filter 实现过滤算法 45 | func (f *PoliticalWordFilter) Filter(content string) bool { 46 | // TODO: 实现算法 47 | return true 48 | } 49 | -------------------------------------------------------------------------------- /15_chain/chain_test.go: -------------------------------------------------------------------------------- 1 | package chain 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestSensitiveWordFilterChain_Filter(t *testing.T) { 10 | chain := &SensitiveWordFilterChain{} 11 | chain.AddFilter(&AdSensitiveWordFilter{}) 12 | assert.Equal(t, false, chain.Filter("test")) 13 | 14 | chain.AddFilter(&PoliticalWordFilter{}) 15 | assert.Equal(t, true, chain.Filter("test")) 16 | } 17 | -------------------------------------------------------------------------------- /16_state/state.go: -------------------------------------------------------------------------------- 1 | // Package state 状态模式 2 | // 笔记请查看: https://lailin.xyz/state.html 3 | // 这是一个工作流的例子,在企业内部或者是学校我们经常会看到很多审批流程 4 | // 假设我们有一个报销的流程: 员工提交报销申请 -> 直属部门领导审批 -> 财务审批 -> 结束 5 | // 在这个审批流中,处在不同的环节就是不同的状态 6 | // 而流程的审批、驳回就是不同的事件 7 | package state 8 | 9 | import "fmt" 10 | 11 | // Machine 状态机 12 | type Machine struct { 13 | state IState 14 | } 15 | 16 | // SetState 更新状态 17 | func (m *Machine) SetState(state IState) { 18 | m.state = state 19 | } 20 | 21 | // GetStateName 获取当前状态 22 | func (m *Machine) GetStateName() string { 23 | return m.state.GetName() 24 | } 25 | 26 | func (m *Machine) Approval() { 27 | m.state.Approval(m) 28 | } 29 | 30 | func (m *Machine) Reject() { 31 | m.state.Reject(m) 32 | } 33 | 34 | // IState 状态 35 | type IState interface { 36 | // 审批通过 37 | Approval(m *Machine) 38 | // 驳回 39 | Reject(m *Machine) 40 | // 获取当前状态名称 41 | GetName() string 42 | } 43 | 44 | // leaderApproveState 直属领导审批 45 | type leaderApproveState struct{} 46 | 47 | // Approval 获取状态名字 48 | func (leaderApproveState) Approval(m *Machine) { 49 | fmt.Println("leader 审批成功") 50 | m.SetState(GetFinanceApproveState()) 51 | } 52 | 53 | // GetName 获取状态名字 54 | func (leaderApproveState) GetName() string { 55 | return "LeaderApproveState" 56 | } 57 | 58 | // Reject 获取状态名字 59 | func (leaderApproveState) Reject(m *Machine) {} 60 | 61 | func GetLeaderApproveState() IState { 62 | return &leaderApproveState{} 63 | } 64 | 65 | // financeApproveState 财务审批 66 | type financeApproveState struct{} 67 | 68 | // Approval 审批通过 69 | func (f financeApproveState) Approval(m *Machine) { 70 | fmt.Println("财务审批成功") 71 | fmt.Println("出发打款操作") 72 | } 73 | 74 | // 拒绝 75 | func (f financeApproveState) Reject(m *Machine) { 76 | m.SetState(GetLeaderApproveState()) 77 | } 78 | 79 | // GetName 获取名字 80 | func (f financeApproveState) GetName() string { 81 | return "FinanceApproveState" 82 | } 83 | 84 | // GetFinanceApproveState GetFinanceApproveState 85 | func GetFinanceApproveState() IState { 86 | return &financeApproveState{} 87 | } 88 | -------------------------------------------------------------------------------- /16_state/state_test.go: -------------------------------------------------------------------------------- 1 | package state 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestMachine_GetStateName(t *testing.T) { 10 | m := &Machine{state: GetLeaderApproveState()} 11 | assert.Equal(t, "LeaderApproveState", m.GetStateName()) 12 | m.Approval() 13 | assert.Equal(t, "FinanceApproveState", m.GetStateName()) 14 | m.Reject() 15 | assert.Equal(t, "LeaderApproveState", m.GetStateName()) 16 | m.Approval() 17 | assert.Equal(t, "FinanceApproveState", m.GetStateName()) 18 | m.Approval() 19 | } 20 | -------------------------------------------------------------------------------- /17_iterator/iterator.go: -------------------------------------------------------------------------------- 1 | package iterator 2 | 3 | // Iterator 迭代器接口 4 | type Iterator interface { 5 | HasNext() bool 6 | Next() 7 | // 获取当前元素,由于 Go 1.15 中还没有泛型,所以我们直接返回 interface{} 8 | CurrentItem() interface{} 9 | } 10 | 11 | // ArrayInt 数组 12 | type ArrayInt []int 13 | 14 | // Iterator 返回迭代器 15 | func (a ArrayInt) Iterator() Iterator { 16 | return &ArrayIntIterator{ 17 | arrayInt: a, 18 | index: 0, 19 | } 20 | } 21 | 22 | // ArrayIntIterator 数组迭代 23 | type ArrayIntIterator struct { 24 | arrayInt ArrayInt 25 | index int 26 | } 27 | 28 | // HasNext 是否有下一个 29 | func (iter *ArrayIntIterator) HasNext() bool { 30 | return iter.index < len(iter.arrayInt)-1 31 | } 32 | 33 | // Next 游标加一 34 | func (iter *ArrayIntIterator) Next() { 35 | iter.index++ 36 | } 37 | 38 | // CurrentItem 获取当前元素 39 | func (iter *ArrayIntIterator) CurrentItem() interface{} { 40 | return iter.arrayInt[iter.index] 41 | } 42 | -------------------------------------------------------------------------------- /17_iterator/iterator_test.go: -------------------------------------------------------------------------------- 1 | package iterator 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestArrayInt_Iterator(t *testing.T) { 10 | data := ArrayInt{1, 3, 5, 7, 8} 11 | iterator := data.Iterator() 12 | // i 用于测试 13 | i := 0 14 | for iterator.HasNext() { 15 | assert.Equal(t, data[i], iterator.CurrentItem()) 16 | iterator.Next() 17 | i++ 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /18_visitor/visitor.go: -------------------------------------------------------------------------------- 1 | package visitor 2 | 3 | import ( 4 | "fmt" 5 | "path" 6 | ) 7 | 8 | // Visitor 访问者 9 | type Visitor interface { 10 | Visit(IResourceFile) error 11 | } 12 | 13 | // IResourceFile IResourceFile 14 | type IResourceFile interface { 15 | Accept(Visitor) error 16 | } 17 | 18 | // NewResourceFile NewResourceFile 19 | func NewResourceFile(filepath string) (IResourceFile, error) { 20 | switch path.Ext(filepath) { 21 | case ".ppt": 22 | return &PPTFile{path: filepath}, nil 23 | case ".pdf": 24 | return &PdfFile{path: filepath}, nil 25 | default: 26 | return nil, fmt.Errorf("not found file type: %s", filepath) 27 | } 28 | } 29 | 30 | // PdfFile PdfFile 31 | type PdfFile struct { 32 | path string 33 | } 34 | 35 | // Accept Accept 36 | func (f *PdfFile) Accept(visitor Visitor) error { 37 | return visitor.Visit(f) 38 | } 39 | 40 | // PPTFile PPTFile 41 | type PPTFile struct { 42 | path string 43 | } 44 | 45 | // Accept Accept 46 | func (f *PPTFile) Accept(visitor Visitor) error { 47 | return visitor.Visit(f) 48 | } 49 | 50 | // Compressor 实现压缩功能 51 | type Compressor struct{} 52 | 53 | // Visit 实现访问者模式方法 54 | // 我们可以发现由于没有函数重载,我们只能通过断言来根据不同的类型调用不同函数 55 | // 但是我们即使不采用访问者模式,我们其实也是可以这么操作的 56 | func (c *Compressor) Visit(r IResourceFile) error { 57 | switch f := r.(type) { 58 | case *PPTFile: 59 | return c.VisitPPTFile(f) 60 | case *PdfFile: 61 | return c.VisitPDFFile(f) 62 | default: 63 | return fmt.Errorf("not found resource typr: %#v", r) 64 | } 65 | } 66 | 67 | // VisitPPTFile VisitPPTFile 68 | func (c *Compressor) VisitPPTFile(f *PPTFile) error { 69 | fmt.Println("this is ppt file") 70 | return nil 71 | } 72 | 73 | // VisitPDFFile VisitPDFFile 74 | func (c *Compressor) VisitPDFFile(f *PdfFile) error { 75 | fmt.Println("this is pdf file") 76 | return nil 77 | } 78 | -------------------------------------------------------------------------------- /18_visitor/visitor_test.go: -------------------------------------------------------------------------------- 1 | package visitor 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestCompressor_Visit(t *testing.T) { 10 | tests := []struct { 11 | name string 12 | path string 13 | wantErr string 14 | }{ 15 | { 16 | name: "pdf", 17 | path: "./xx.pdf", 18 | }, 19 | { 20 | name: "ppt", 21 | path: "./xx.ppt", 22 | }, 23 | { 24 | name: "404", 25 | path: "./xx.xx", 26 | wantErr: "not found file type", 27 | }, 28 | } 29 | 30 | for _, tt := range tests { 31 | t.Run(tt.name, func(t *testing.T) { 32 | f, err := NewResourceFile(tt.path) 33 | if tt.wantErr != "" { 34 | require.Error(t, err) 35 | require.Contains(t, err.Error(), tt.wantErr) 36 | return 37 | } 38 | 39 | require.NoError(t, err) 40 | compressor := &Compressor{} 41 | f.Accept(compressor) 42 | }) 43 | } 44 | } 45 | 46 | // 不用 Accept 其实也是可以的 47 | func TestCompressor_Visit2(t *testing.T) { 48 | tests := []struct { 49 | name string 50 | path string 51 | wantErr string 52 | }{ 53 | { 54 | name: "pdf", 55 | path: "./xx.pdf", 56 | }, 57 | { 58 | name: "ppt", 59 | path: "./xx.ppt", 60 | }, 61 | { 62 | name: "404", 63 | path: "./xx.xx", 64 | wantErr: "not found file type", 65 | }, 66 | } 67 | 68 | for _, tt := range tests { 69 | t.Run(tt.name, func(t *testing.T) { 70 | f, err := NewResourceFile(tt.path) 71 | if tt.wantErr != "" { 72 | require.Error(t, err) 73 | require.Contains(t, err.Error(), tt.wantErr) 74 | return 75 | } 76 | 77 | require.NoError(t, err) 78 | compressor := &Compressor{} 79 | compressor.Visit(f) 80 | }) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /19_memento/memento.go: -------------------------------------------------------------------------------- 1 | // Package memento 备忘录模式 2 | // 下面这个例子采用原课程的例子,一个输入程序 3 | // 如果输入 :list 则显示当前保存的内容 4 | // 如果输入 :undo 则删除上一次的输入 5 | // 如果输入其他的内容则追加保存 6 | package memento 7 | 8 | // InputText 用于保存数据 9 | type InputText struct { 10 | content string 11 | } 12 | 13 | // Append 追加数据 14 | func (in *InputText) Append(content string) { 15 | in.content += content 16 | } 17 | 18 | // GetText 获取数据 19 | func (in *InputText) GetText() string { 20 | return in.content 21 | } 22 | 23 | // Snapshot 创建快照 24 | func (in *InputText) Snapshot() *Snapshot { 25 | return &Snapshot{content: in.content} 26 | } 27 | 28 | // Restore 从快照中恢复 29 | func (in *InputText) Restore(s *Snapshot) { 30 | in.content = s.GetText() 31 | } 32 | 33 | // Snapshot 快照,用于存储数据快照 34 | // 对于快照来说,只能不能被外部(不同包)修改,只能获取数据,满足封装的特性 35 | type Snapshot struct { 36 | content string 37 | } 38 | 39 | // GetText GetText 40 | func (s *Snapshot) GetText() string { 41 | return s.content 42 | } 43 | -------------------------------------------------------------------------------- /19_memento/memento_test.go: -------------------------------------------------------------------------------- 1 | package memento 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestDemo(t *testing.T) { 10 | in := &InputText{} 11 | snapshots := []*Snapshot{} 12 | 13 | tests := []struct { 14 | input string 15 | want string 16 | }{ 17 | { 18 | input: ":list", 19 | want: "", 20 | }, 21 | { 22 | input: "hello", 23 | want: "", 24 | }, 25 | { 26 | input: ":list", 27 | want: "hello", 28 | }, 29 | { 30 | input: "world", 31 | want: "", 32 | }, 33 | { 34 | input: ":list", 35 | want: "helloworld", 36 | }, 37 | { 38 | input: ":undo", 39 | want: "", 40 | }, 41 | { 42 | input: ":list", 43 | want: "hello", 44 | }, 45 | } 46 | for _, tt := range tests { 47 | t.Run(tt.input, func(t *testing.T) { 48 | switch tt.input { 49 | case ":list": 50 | assert.Equal(t, tt.want, in.GetText()) 51 | case ":undo": 52 | in.Restore(snapshots[len(snapshots)-1]) 53 | snapshots = snapshots[:len(snapshots)-1] 54 | default: 55 | snapshots = append(snapshots, in.Snapshot()) 56 | in.Append(tt.input) 57 | } 58 | }) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /20_command/command.go: -------------------------------------------------------------------------------- 1 | // Package command 命令模式 2 | // Blog: https://lailin.xyz/post/command.html 3 | // 这是示例一,采用将函数封装为对象的方式实现, 4 | // 示例说明: 5 | // 假设现在有一个游戏服务,我们正在实现一个游戏后端 6 | // 使用一个 goroutine 不断接收来自客户端请求的命令,并且将它放置到一个队列当中 7 | // 然后我们在另外一个 goroutine 中来执行它 8 | package command 9 | 10 | import "fmt" 11 | 12 | // ICommand 命令 13 | type ICommand interface { 14 | Execute() error 15 | } 16 | 17 | // StartCommand 游戏开始运行 18 | type StartCommand struct{} 19 | 20 | // NewStartCommand NewStartCommand 21 | func NewStartCommand( /*正常情况下这里会有一些参数*/ ) *StartCommand { 22 | return &StartCommand{} 23 | } 24 | 25 | // Execute Execute 26 | func (c *StartCommand) Execute() error { 27 | fmt.Println("game start") 28 | return nil 29 | } 30 | 31 | // ArchiveCommand 游戏存档 32 | type ArchiveCommand struct{} 33 | 34 | // NewArchiveCommand NewArchiveCommand 35 | func NewArchiveCommand( /*正常情况下这里会有一些参数*/ ) *ArchiveCommand { 36 | return &ArchiveCommand{} 37 | } 38 | 39 | // Execute Execute 40 | func (c *ArchiveCommand) Execute() error { 41 | fmt.Println("game archive") 42 | return nil 43 | } 44 | -------------------------------------------------------------------------------- /20_command/command_func.go: -------------------------------------------------------------------------------- 1 | // Package command 命令模式 2 | // Blog: https://lailin.xyz/post/command.html 3 | // 这是示例二,采用将直接返回一个函数,不用对象 4 | // 示例说明: 5 | // 假设现在有一个游戏服务,我们正在实现一个游戏后端 6 | // 使用一个 goroutine 不断接收来自客户端请求的命令,并且将它放置到一个队列当中 7 | // 然后我们在另外一个 goroutine 中来执行它 8 | package command 9 | 10 | import "fmt" 11 | 12 | // Command 命令 13 | type Command func() error 14 | 15 | // StartCommandFunc 返回一个 Command 命令 16 | // 是因为正常情况下不会是这么简单的函数 17 | // 一般都会有一些参数 18 | func StartCommandFunc() Command { 19 | return func() error { 20 | fmt.Println("game start") 21 | return nil 22 | } 23 | } 24 | 25 | // ArchiveCommandFunc ArchiveCommandFunc 26 | func ArchiveCommandFunc() Command { 27 | return func() error { 28 | fmt.Println("game archive") 29 | return nil 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /20_command/command_func_test.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestDemoFunc(t *testing.T) { 10 | // 用于测试,模拟来自客户端的事件 11 | eventChan := make(chan string) 12 | go func() { 13 | events := []string{"start", "archive", "start", "archive", "start", "start"} 14 | for _, e := range events { 15 | eventChan <- e 16 | } 17 | 18 | }() 19 | defer close(eventChan) 20 | 21 | // 使用命令队列缓存命令 22 | commands := make(chan Command, 1000) 23 | defer close(commands) 24 | 25 | go func() { 26 | for { 27 | // 从请求或者其他地方获取相关事件参数 28 | event, ok := <-eventChan 29 | if !ok { 30 | return 31 | } 32 | 33 | var command Command 34 | switch event { 35 | case "start": 36 | command = StartCommandFunc() 37 | case "archive": 38 | command = ArchiveCommandFunc() 39 | } 40 | 41 | // 将命令入队 42 | commands <- command 43 | } 44 | }() 45 | 46 | for { 47 | select { 48 | case c := <-commands: 49 | c() 50 | case <-time.After(1 * time.Second): 51 | fmt.Println("timeout 1s") 52 | return 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /20_command/command_test.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestDemo(t *testing.T) { 10 | // 用于测试,模拟来自客户端的事件 11 | eventChan := make(chan string) 12 | go func() { 13 | events := []string{"start", "archive", "start", "archive", "start", "start"} 14 | for _, e := range events { 15 | eventChan <- e 16 | } 17 | }() 18 | defer close(eventChan) 19 | 20 | // 使用命令队列缓存命令 21 | commands := make(chan ICommand, 1000) 22 | defer close(commands) 23 | 24 | go func() { 25 | for { 26 | // 从请求或者其他地方获取相关事件参数 27 | event, ok := <-eventChan 28 | if !ok { 29 | return 30 | } 31 | 32 | var command ICommand 33 | switch event { 34 | case "start": 35 | command = NewStartCommand() 36 | case "archive": 37 | command = NewArchiveCommand() 38 | } 39 | 40 | // 将命令入队 41 | commands <- command 42 | } 43 | }() 44 | 45 | for { 46 | select { 47 | case c := <-commands: 48 | c.Execute() 49 | case <-time.After(1 * time.Second): 50 | fmt.Println("timeout 1s") 51 | return 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /21_interpreter/interpreter.go: -------------------------------------------------------------------------------- 1 | // Package interpreter 解释器模式 2 | // 采用原课程的示例, 并且做了一下简化 3 | // 假设我们现在有一个监控系统 4 | // 现在需要实现一个告警模块,可以根据输入的告警规则来决定是否触发告警 5 | // 告警规则支持 &&、>、< 3种运算符 6 | // 其中 >、< 优先级比 && 更高 7 | package interpreter 8 | 9 | import ( 10 | "fmt" 11 | "regexp" 12 | "strconv" 13 | "strings" 14 | ) 15 | 16 | // AlertRule 告警规则 17 | type AlertRule struct { 18 | expression IExpression 19 | } 20 | 21 | // NewAlertRule NewAlertRule 22 | func NewAlertRule(rule string) (*AlertRule, error) { 23 | exp, err := NewAndExpression(rule) 24 | return &AlertRule{expression: exp}, err 25 | } 26 | 27 | // Interpret 判断告警是否触发 28 | func (r AlertRule) Interpret(stats map[string]float64) bool { 29 | return r.expression.Interpret(stats) 30 | } 31 | 32 | // IExpression 表达式接口 33 | type IExpression interface { 34 | Interpret(stats map[string]float64) bool 35 | } 36 | 37 | // GreaterExpression > 38 | type GreaterExpression struct { 39 | key string 40 | value float64 41 | } 42 | 43 | // Interpret Interpret 44 | func (g GreaterExpression) Interpret(stats map[string]float64) bool { 45 | v, ok := stats[g.key] 46 | if !ok { 47 | return false 48 | } 49 | return v > g.value 50 | } 51 | 52 | // NewGreaterExpression NewGreaterExpression 53 | func NewGreaterExpression(exp string) (*GreaterExpression, error) { 54 | data := regexp.MustCompile(`\s+`).Split(strings.TrimSpace(exp), -1) 55 | if len(data) != 3 || data[1] != ">" { 56 | return nil, fmt.Errorf("exp is invalid: %s", exp) 57 | } 58 | 59 | val, err := strconv.ParseFloat(data[2], 10) 60 | if err != nil { 61 | return nil, fmt.Errorf("exp is invalid: %s", exp) 62 | } 63 | 64 | return &GreaterExpression{ 65 | key: data[0], 66 | value: val, 67 | }, nil 68 | } 69 | 70 | // LessExpression < 71 | type LessExpression struct { 72 | key string 73 | value float64 74 | } 75 | 76 | // Interpret Interpret 77 | func (g LessExpression) Interpret(stats map[string]float64) bool { 78 | v, ok := stats[g.key] 79 | if !ok { 80 | return false 81 | } 82 | return v < g.value 83 | } 84 | 85 | // NewLessExpression NewLessExpression 86 | func NewLessExpression(exp string) (*LessExpression, error) { 87 | data := regexp.MustCompile(`\s+`).Split(strings.TrimSpace(exp), -1) 88 | if len(data) != 3 || data[1] != "<" { 89 | return nil, fmt.Errorf("exp is invalid: %s", exp) 90 | } 91 | 92 | val, err := strconv.ParseFloat(data[2], 10) 93 | if err != nil { 94 | return nil, fmt.Errorf("exp is invalid: %s", exp) 95 | } 96 | 97 | return &LessExpression{ 98 | key: data[0], 99 | value: val, 100 | }, nil 101 | } 102 | 103 | // AndExpression && 104 | type AndExpression struct { 105 | expressions []IExpression 106 | } 107 | 108 | // Interpret Interpret 109 | func (e AndExpression) Interpret(stats map[string]float64) bool { 110 | for _, expression := range e.expressions { 111 | if !expression.Interpret(stats) { 112 | return false 113 | } 114 | } 115 | return true 116 | } 117 | 118 | // NewAndExpression NewAndExpression 119 | func NewAndExpression(exp string) (*AndExpression, error) { 120 | exps := strings.Split(exp, "&&") 121 | expressions := make([]IExpression, len(exps)) 122 | 123 | for i, e := range exps { 124 | var expression IExpression 125 | var err error 126 | 127 | switch { 128 | case strings.Contains(e, ">"): 129 | expression, err = NewGreaterExpression(e) 130 | case strings.Contains(e, "<"): 131 | expression, err = NewLessExpression(e) 132 | default: 133 | err = fmt.Errorf("exp is invalid: %s", exp) 134 | } 135 | 136 | if err != nil { 137 | return nil, err 138 | } 139 | 140 | expressions[i] = expression 141 | } 142 | 143 | return &AndExpression{expressions: expressions}, nil 144 | } 145 | -------------------------------------------------------------------------------- /21_interpreter/interpreter_test.go: -------------------------------------------------------------------------------- 1 | package interpreter 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestAlertRule_Interpret(t *testing.T) { 11 | stats := map[string]float64{ 12 | "a": 1, 13 | "b": 2, 14 | "c": 3, 15 | } 16 | tests := []struct { 17 | name string 18 | stats map[string]float64 19 | rule string 20 | want bool 21 | }{ 22 | { 23 | name: "case1", 24 | stats: stats, 25 | rule: "a > 1 && b > 10 && c < 5", 26 | want: false, 27 | }, 28 | { 29 | name: "case2", 30 | stats: stats, 31 | rule: "a < 2 && b > 10 && c < 5", 32 | want: false, 33 | }, 34 | { 35 | name: "case3", 36 | stats: stats, 37 | rule: "a < 5 && b > 1 && c < 10", 38 | want: true, 39 | }, 40 | } 41 | for _, tt := range tests { 42 | t.Run(tt.name, func(t *testing.T) { 43 | r, err := NewAlertRule(tt.rule) 44 | require.NoError(t, err) 45 | assert.Equal(t, tt.want, r.Interpret(tt.stats)) 46 | }) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /22_mediator/mediator.go: -------------------------------------------------------------------------------- 1 | // Package mediator 中介模式 2 | // 采用原课程的示例,并且做了一些裁剪 3 | // 假设我们现在有一个较为复杂的对话框,里面包括,登录组件,注册组件,以及选择框 4 | // 当选择框选择“登录”时,展示登录相关组件 5 | // 当选择框选择“注册”时,展示注册相关组件 6 | package mediator 7 | 8 | import ( 9 | "fmt" 10 | "reflect" 11 | ) 12 | 13 | // Input 假设这表示一个输入框 14 | type Input string 15 | 16 | // String String 17 | func (i Input) String() string { 18 | return string(i) 19 | } 20 | 21 | // Selection 假设这表示一个选择框 22 | type Selection string 23 | 24 | // Selected 当前选中的对象 25 | func (s Selection) Selected() string { 26 | return string(s) 27 | } 28 | 29 | // Button 假设这表示一个按钮 30 | type Button struct { 31 | onClick func() 32 | } 33 | 34 | // SetOnClick 添加点击事件回调 35 | func (b *Button) SetOnClick(f func()) { 36 | b.onClick = f 37 | } 38 | 39 | // IMediator 中介模式接口 40 | type IMediator interface { 41 | HandleEvent(component interface{}) 42 | } 43 | 44 | // Dialog 对话框组件 45 | type Dialog struct { 46 | LoginButton *Button 47 | RegButton *Button 48 | Selection *Selection 49 | UsernameInput *Input 50 | PasswordInput *Input 51 | RepeatPasswordInput *Input 52 | } 53 | 54 | // HandleEvent HandleEvent 55 | func (d *Dialog) HandleEvent(component interface{}) { 56 | switch { 57 | case reflect.DeepEqual(component, d.Selection): 58 | if d.Selection.Selected() == "登录" { 59 | fmt.Println("select login") 60 | fmt.Printf("show: %s\n", d.UsernameInput) 61 | fmt.Printf("show: %s\n", d.PasswordInput) 62 | } else if d.Selection.Selected() == "注册" { 63 | fmt.Println("select register") 64 | fmt.Printf("show: %s\n", d.UsernameInput) 65 | fmt.Printf("show: %s\n", d.PasswordInput) 66 | fmt.Printf("show: %s\n", d.RepeatPasswordInput) 67 | } 68 | // others, 如果点击了登录按钮,注册按钮 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /22_mediator/mediator_test.go: -------------------------------------------------------------------------------- 1 | package mediator 2 | 3 | import "testing" 4 | 5 | func TestDemo(t *testing.T) { 6 | usernameInput := Input("username input") 7 | passwordInput := Input("password input") 8 | repeatPwdInput := Input("repeat password input") 9 | 10 | selection := Selection("登录") 11 | d := &Dialog{ 12 | Selection: &selection, 13 | UsernameInput: &usernameInput, 14 | PasswordInput: &passwordInput, 15 | RepeatPasswordInput: &repeatPwdInput, 16 | } 17 | d.HandleEvent(&selection) 18 | 19 | regSelection := Selection("注册") 20 | d.Selection = ®Selection 21 | d.HandleEvent(®Selection) 22 | } 23 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mohuishou/go-design-pattern 2 | 3 | go 1.14 4 | 5 | require github.com/stretchr/testify v1.6.1 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 4 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 5 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 6 | github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= 7 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 8 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 9 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 10 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 11 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 12 | -------------------------------------------------------------------------------- /imgs/img01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohuishou/go-design-pattern/2dd60236ec711b599a935f9bd00a0e848e982929/imgs/img01.jpg -------------------------------------------------------------------------------- /imgs/img02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohuishou/go-design-pattern/2dd60236ec711b599a935f9bd00a0e848e982929/imgs/img02.jpg -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # go-design-pattern 2 | 3 | 更多系列文章可以扫描下方二维码关注博主 4 | 5 | ![微信公众号](https://mohuishou-blog-sz.oss-cn-shenzhen.aliyuncs.com/custom/wechat_white.png) 6 | 7 | ## 总结 8 | 9 | 原本预计是在十月底更新完毕这个系列,到今天是11-05,晚了几天,不过也还好,这是第一次这么密集的去更新博客上的内容,更多的是以笔记的形式来呈现,加上这篇一共24篇文章差不多两个半月的时间,平均每周输出两篇,感觉还是不错。后续可能会视情况不定期的更新一些实战内容,也有可能没有。接下来下一个系列应该是数据结构与算法,包含对 Go 中一些底层数据和标准库包的学习,例如 slice, sort 等等。 10 | 11 | 话说回来,回头再看学习设计模式我们究竟需要学习一些什么? 12 | 13 | - 写 Go 需要使用到设计模式么? 14 | - 需要,但是切记请勿使用其他语言的方式来写 Go 15 | - 如果看过之前的一些文章,就会发现类似 JAVA 的这些面向对象语言中的某些设计模式的写法在 Go 中会十分的别扭 16 | - 但是 Go 不需要设计模式么?不是的,设计模式的思想是想通的,并且我们一直都在使用,例如我们常见的对象创建方式 `NewXXX` 这其实就是一个简单工厂 17 | - 设计模式学习的重点是什么? 18 | - 设计原则,以及设计模式的使用场景和优缺点,实现相对来说还没有那么重要 19 | - 如果是常见的设计模式是武术招式,那么设计原则就是内功心法,没有内功心法那么招式套路也就是花架子 20 | - 熟练掌握不同设计模式的使用场景可以帮助我们学会见招拆招,灵活应用而不是只会套路 21 | - **最后设计模式不是银弹,不要拿着🔨就觉得哪里都像是钉子,不要过早优化,持续重构才是正道** 22 | 23 | ### 设计原则 24 | 25 | > 同时这也是 Code Review 的重要标准之一 26 | 27 |
28 | 点击展开设计原则 29 | 30 | ![设计原则](imgs/img01.jpg) 31 | 32 |
33 | 34 | ### 设计模式 35 | 36 |
37 | 点击展开设计模式 38 | 39 | ![设计模式](imgs/img02.jpg) 40 | 41 |
42 | 43 | 44 | 45 | ## Go设计模式 46 | 47 | - 单例模式包含饿汉式和懒汉式两种实现 48 | - 工厂模式包含简单工厂、工厂方法、抽象工厂、DI容器 49 | - 代理模式包含静态代理、动态代理(采用 go generate 模拟) 50 | - 观察者模式包含观察者模式、eventbus 51 | 52 | | **类型** | **设计模式(Github)** | **常用** | **博客** | 53 | | :--------: | :-----------------------------------------------------------------------------------------------------------------------: | :------: | :-----------------------------------------------------------------------------------: | 54 | | **创建型** | [单例模式(Singleton Design Pattern)](https://github.com/mohuishou/go-design-pattern/blob/master/01_singleton) | ✅ | [Go设计模式01-单例模式](https://lailin.xyz/post/singleton.html) | 55 | | | [工厂模式(Factory Design Pattern)](https://github.com/mohuishou/go-design-pattern/blob/master/02_factory) | ✅ | [Go设计模式02-工厂模式&DI容器](https://lailin.xyz/post/factory.html) | 56 | | | [建造者模式(Builder Design Pattern)](https://github.com/mohuishou/go-design-pattern/blob/master/03_builder) | ✅ | [Go设计模式03-建造者模式](https://lailin.xyz/post/builder.html) | 57 | | | [原型模式(Prototype Design Pattern)](https://github.com/mohuishou/go-design-pattern/blob/master/04_prototype) | ❌ | [Go设计模式04-原型模式](https://lailin.xyz/post/prototype.html) | 58 | | **结构型** | [代理模式(Proxy Design Pattern)](https://github.com/mohuishou/go-design-pattern/blob/master/05_proxy) | ✅ | [Go设计模式06-代理模式(generate实现类似动态代理)](https://lailin.xyz/post/proxy.html) | 59 | | | [桥接模式(Bridge Design Pattern)](https://github.com/mohuishou/go-design-pattern/blob/master/06_bridge) | ✅ | [Go设计模式07-桥接模式](https://lailin.xyz/post/bridge.html) | 60 | | | [装饰器模式(Decorator Design Pattern)](https://github.com/mohuishou/go-design-pattern/blob/master/07_decorator) | ✅ | [Go设计模式08-装饰器模式](https://lailin.xyz/post/decorator.html) | 61 | | | [适配器模式(Adapter Design Pattern)](https://github.com/mohuishou/go-design-pattern/blob/master/08_adapter) | ✅ | [Go设计模式09-适配器模式](https://lailin.xyz/post/adapter.html) | 62 | | | [门面模式(Facade Design Pattern)](https://github.com/mohuishou/go-design-pattern/blob/master/09_facade) | ❌ | [Go设计模式10-门面模式](https://lailin.xyz/post/facade.html) | 63 | | | [组合模式(Composite Design Pattern)](https://github.com/mohuishou/go-design-pattern/blob/master/10_composite) | ❌ | [Go设计模式11-组合模式](https://lailin.xyz/post/composite.html) | 64 | | | [享元模式(Flyweight Design Pattern)](https://github.com/mohuishou/go-design-pattern/blob/master/11_flyweight) | ❌ | [Go设计模式12-享元模式](https://lailin.xyz/post/flyweight.html) | 65 | | **行为型** | [观察者模式(Observer Design Pattern)](https://github.com/mohuishou/go-design-pattern/blob/master/12_observer) | ✅ | [Go设计模式13-观察者模式(实现简单的EventBus)](https://lailin.xyz/post/observer.html) | 66 | | | [模板模式(Template Method Design Pattern)](https://github.com/mohuishou/go-design-pattern/blob/master/13_template) | ✅ | [Go模板模式14-模板模式](https://lailin.xyz/post/template.html) | 67 | | | [策略模式(Strategy Method Design Pattern)](https://github.com/mohuishou/go-design-pattern/blob/master/14_strategy) | ✅ | [Go设计模式15-策略模式](https://lailin.xyz/post/strategy.html) | 68 | | | [职责链模式(Chain Of Responsibility Design Pattern)](https://github.com/mohuishou/go-design-pattern/blob/master/15_chain) | ✅ | [Go设计模式16-职责链模式(Gin的中间件实现)](https://lailin.xyz/post/chain.html) | 69 | | | [状态模式(State Design Pattern)](https://github.com/mohuishou/go-design-pattern/blob/master/16_state) | ✅ | [Go设计模式17-状态模式](https://lailin.xyz/post/state.html) | 70 | | | [迭代器模式(Iterator Design Pattern)](https://github.com/mohuishou/go-design-pattern/blob/master/17_iterator) | ✅ | [Go设计模式18-迭代器模式](https://lailin.xyz/post/iterator.html) | 71 | | | [访问者模式(Visitor Design Pattern)](https://github.com/mohuishou/go-design-pattern/blob/master/18_visitor/visitor.go) | ❌ | [Go设计模式19-访问者模式](https://lailin.xyz/post/visitor.html) | 72 | | | [备忘录模式(Memento Design Pattern)](https://github.com/mohuishou/go-design-pattern/blob/master/19_memento) | ❌ | [Go设计模式20-备忘录模式](https://lailin.xyz/post/memento.html) | 73 | | | [命令模式(Command Design Pattern)](https://github.com/mohuishou/go-design-pattern/blob/master/20_command) | ❌ | [Go设计模式21-命令模式](https://lailin.xyz/post/command.html) | 74 | | | [解释器模式(Interpreter Design Pattern)](https://github.com/mohuishou/go-design-pattern/blob/master/21_interpreter) | ❌ | [Go设计模式22-解释器模式](https://lailin.xyz/post/interpreter.html) | 75 | | | [中介模式(Mediator Design Pattern)](https://github.com/mohuishou/go-design-pattern/blob/master/22_mediator) | ❌ | [Go设计模式23-中介模式](https://lailin.xyz/post/mediator.html) | -------------------------------------------------------------------------------- /readme_en.md: -------------------------------------------------------------------------------- 1 | # go-design-pattern 2 | --------------------------------------------------------------------------------