├── go.mod ├── generator └── main.go ├── factory ├── main.go ├── factory │ └── factory.go └── mermaid-diagram-20190801184356.svg ├── decorator └── main.go └── observer └── main.go /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/tensor-programming/pattern-tutorial 2 | 3 | go 1.12 4 | -------------------------------------------------------------------------------- /generator/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func fib(n int) chan int { 6 | out := make(chan int) 7 | 8 | go func() { 9 | defer close(out) 10 | for i, j := 0, 1; i < n; i, j = i+j, i { 11 | out <- i 12 | } 13 | 14 | }() 15 | 16 | return out 17 | } 18 | 19 | // 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ... 20 | 21 | func main() { 22 | for x := range fib(10000000) { 23 | fmt.Println(x) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /factory/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | 7 | "github.com/tensor-programming/pattern-tutorial/factory" 8 | ) 9 | 10 | func SetupConstructors(env string) (factory.Database, factory.FileSystem) { 11 | fs := factory.AbstractFactory("filesystem") 12 | db := factory.AbstractFactory("database") 13 | 14 | return db(env).(factory.Database), 15 | fs(env).(factory.FileSystem) 16 | } 17 | 18 | func main() { 19 | env1 := "production" 20 | env2 := "development" 21 | 22 | db1, fs1 := SetupConstructors(env1) 23 | db2, fs2 := SetupConstructors(env2) 24 | 25 | // db1 := factory.DatabaseFactory(env1) 26 | // db2 := factory.DatabaseFactory(env2) 27 | 28 | db1.PutData("test", "this is mongodb") 29 | fmt.Println(db1.GetData("test")) 30 | 31 | db2.PutData("test", "this is sqlite") 32 | fmt.Println(db2.GetData("test")) 33 | 34 | fs1.CreateFile("../example/testntfs.txt") 35 | fmt.Println(fs1.FindFile("../example/testntfs.txt")) 36 | 37 | fs2.CreateFile("../example/testext4.txt") 38 | fmt.Println(fs2.FindFile("../example/testext4.txt")) 39 | 40 | fmt.Println(reflect.TypeOf(db1).Name()) 41 | fmt.Println(reflect.TypeOf(&db1).Elem()) 42 | fmt.Println(reflect.TypeOf(db2).Name()) 43 | fmt.Println(reflect.TypeOf(&db2).Elem()) 44 | 45 | fmt.Println(reflect.TypeOf(fs1).Name()) 46 | fmt.Println(reflect.TypeOf(&fs1).Elem()) 47 | fmt.Println(reflect.TypeOf(fs2).Name()) 48 | fmt.Println(reflect.TypeOf(&fs2).Elem()) 49 | } 50 | -------------------------------------------------------------------------------- /decorator/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "math" 7 | "os" 8 | "sync" 9 | "time" 10 | ) 11 | 12 | type piFunc func(int) float64 13 | 14 | // logger(cache(Pi(n))) 15 | func wraplogger(fun piFunc, logger *log.Logger) piFunc { 16 | return func(n int) float64 { 17 | fn := func(n int) (result float64) { 18 | defer func(t time.Time) { 19 | logger.Printf("took=%v, n=%v, result=%v", time.Since(t), n, result) 20 | }(time.Now()) 21 | 22 | return fun(n) 23 | } 24 | return fn(n) 25 | } 26 | } 27 | 28 | // cache(logger(Pi(n))) 29 | func wrapcache(fun piFunc, cache *sync.Map) piFunc { 30 | return func(n int) float64 { 31 | fn := func(n int) float64 { 32 | key := fmt.Sprintf("n=%d", n) 33 | val, ok := cache.Load(key) 34 | if ok { 35 | return val.(float64) 36 | } 37 | result := fun(n) 38 | cache.Store(key, result) 39 | return result 40 | } 41 | return fn(n) 42 | } 43 | } 44 | 45 | func Pi(n int) float64 { 46 | ch := make(chan float64) 47 | 48 | for k := 0; k <= n; k++ { 49 | go func(ch chan float64, k float64) { 50 | ch <- 4 * math.Pow(-1, k) / (2*k + 1) 51 | }(ch, float64(k)) 52 | } 53 | 54 | result := 0.0 55 | for k := 0; k <= n; k++ { 56 | result += <-ch 57 | } 58 | 59 | return result 60 | } 61 | 62 | func divide(n int) float64 { 63 | return float64(n / 2) 64 | } 65 | 66 | func main() { 67 | f := wrapcache(Pi, &sync.Map{}) 68 | g := wraplogger(f, log.New(os.Stdout, "Test ", 1)) 69 | 70 | g(100000) 71 | g(20000) 72 | g(100000) 73 | 74 | f = wrapcache(divide, &sync.Map{}) 75 | g = wraplogger(f, log.New(os.Stdout, "Divide ", 1)) 76 | 77 | g(10000) 78 | g(2000) 79 | g(10) 80 | g(10000) 81 | 82 | } 83 | -------------------------------------------------------------------------------- /observer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | type ( 10 | Event struct { 11 | data int 12 | } 13 | 14 | Observer interface { 15 | NotifyCallback(Event) 16 | } 17 | 18 | Subject interface { 19 | AddListener(Observer) 20 | RemoveListener(Observer) 21 | Notify(Event) 22 | } 23 | 24 | eventObserver struct { 25 | id int 26 | time time.Time 27 | } 28 | 29 | eventSubject struct { 30 | observers sync.Map 31 | } 32 | ) 33 | 34 | func (e *eventObserver) NotifyCallback(event Event) { 35 | fmt.Printf("Observer: %d Recieved: %d after %v\n", e.id, event.data, time.Since(e.time)) 36 | } 37 | 38 | func (s *eventSubject) AddListener(obs Observer) { 39 | s.observers.Store(obs, struct{}{}) 40 | } 41 | 42 | func (s *eventSubject) RemoveListener(obs Observer) { 43 | s.observers.Delete(obs) 44 | } 45 | 46 | func (s *eventSubject) Notify(event Event) { 47 | s.observers.Range(func(key interface{}, value interface{}) bool { 48 | if key == nil || value == nil { 49 | return false 50 | } 51 | 52 | key.(Observer).NotifyCallback(event) 53 | return true 54 | }) 55 | 56 | } 57 | 58 | func fib(n int) chan int { 59 | out := make(chan int) 60 | 61 | go func() { 62 | defer close(out) 63 | for i, j := 0, 1; i < n; i, j = i+j, i { 64 | out <- i 65 | } 66 | 67 | }() 68 | 69 | return out 70 | } 71 | 72 | // 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ... 73 | 74 | func main() { 75 | n := eventSubject{ 76 | observers: sync.Map{}, 77 | } 78 | 79 | t := time.Now() 80 | 81 | var obs1 = eventObserver{id: 1, time: t} 82 | var obs2 = eventObserver{id: 2, time: t} 83 | n.AddListener(&obs1) 84 | n.AddListener(&obs2) 85 | 86 | go func() { 87 | select { 88 | case <-time.After(time.Millisecond * 10): 89 | n.RemoveListener(&obs1) 90 | } 91 | }() 92 | 93 | for x := range fib(100000) { 94 | n.Notify(Event{data: x}) 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /factory/factory/factory.go: -------------------------------------------------------------------------------- 1 | package factory 2 | 3 | import "fmt" 4 | 5 | type ( 6 | mongoDB struct { 7 | database map[string]string 8 | } 9 | 10 | sqlite struct { 11 | database map[string]string 12 | } 13 | 14 | file struct { 15 | name string 16 | content string 17 | } 18 | 19 | ntfs struct { 20 | files map[string]file 21 | } 22 | 23 | ext4 struct { 24 | files map[string]file 25 | } 26 | 27 | FileSystem interface { 28 | CreateFile(string) 29 | FindFile(string) file 30 | } 31 | 32 | Database interface { 33 | GetData(string) string 34 | PutData(string, string) 35 | } 36 | 37 | Factory func(string) interface{} 38 | ) 39 | 40 | func (mdb mongoDB) GetData(query string) string { 41 | if _, ok := mdb.database[query]; !ok { 42 | return "" 43 | } 44 | 45 | fmt.Println("MongoDB") 46 | return mdb.database[query] 47 | } 48 | 49 | func (sql sqlite) GetData(query string) string { 50 | if _, ok := sql.database[query]; !ok { 51 | return "" 52 | } 53 | 54 | fmt.Println("Sqlite") 55 | return sql.database[query] 56 | } 57 | 58 | func (mdb mongoDB) PutData(query string, data string) { 59 | mdb.database[query] = data 60 | } 61 | 62 | func (sql sqlite) PutData(query string, data string) { 63 | sql.database[query] = data 64 | } 65 | 66 | func (ntfs ntfs) CreateFile(path string) { 67 | file := file{content: "NTFS file", name: path} 68 | ntfs.files[path] = file 69 | fmt.Println("NTFS") 70 | } 71 | 72 | func (ext ext4) CreateFile(path string) { 73 | file := file{content: "EXT4 file", name: path} 74 | ext.files[path] = file 75 | fmt.Println("EXT4") 76 | } 77 | 78 | func (ntfs ntfs) FindFile(path string) file { 79 | if _, ok := ntfs.files[path]; !ok { 80 | return file{} 81 | } 82 | 83 | return ntfs.files[path] 84 | } 85 | 86 | func (ext ext4) FindFile(path string) file { 87 | if _, ok := ext.files[path]; !ok { 88 | return file{} 89 | } 90 | 91 | return ext.files[path] 92 | } 93 | 94 | func FilesystemFactory(env string) interface{} { 95 | switch env { 96 | case "production": 97 | return ntfs{ 98 | files: make(map[string]file), 99 | } 100 | case "development": 101 | return ext4{ 102 | files: make(map[string]file), 103 | } 104 | default: 105 | return nil 106 | } 107 | } 108 | 109 | func DatabaseFactory(env string) interface{} { 110 | switch env { 111 | case "production": 112 | return mongoDB{ 113 | database: make(map[string]string), 114 | } 115 | case "development": 116 | return sqlite{ 117 | database: make(map[string]string), 118 | } 119 | default: 120 | return nil 121 | } 122 | } 123 | 124 | func AbstractFactory(fact string) Factory { 125 | switch fact { 126 | case "database": 127 | return DatabaseFactory 128 | case "filesystem": 129 | return FilesystemFactory 130 | default: 131 | return nil 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /factory/mermaid-diagram-20190801184356.svg: -------------------------------------------------------------------------------- 1 | DatabaseInterfaceGetData(string) stringPutData(string)MongoDbGetData(string) stringPutData(string)SqliteGetData(string) stringPutData(string)DatabaseFactoryfunction(string) DatabaseDatabaseGetData(string) stringPutData(string)ClientDatabaseFactory() DatabaseImplementsImplementscreates --------------------------------------------------------------------------------