├── .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 | 
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 | 
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 | 
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 | 
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 | 
31 |
32 |
33 |
34 | ### 设计模式
35 |
36 |
37 | 点击展开设计模式
38 |
39 | 
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 |
--------------------------------------------------------------------------------