├── .gitignore
├── 01-implement-interface-fast
└── main.go
├── 02-variadic-functions-with-silce
└── main.go
├── 03-cast-interface
└── main.go
├── 04-named-return
└── main.go
├── 05-options-pattern
├── config_way.go
├── main.go
└── options_way.go
├── 06-range-expression
└── main.go
├── 07-map-trap
├── main.go
└── map_struct.go.fake
├── 08-map-struct-update
└── main.go
├── 09-no-class-Inheritance
└── main.go
├── 10-interface-compare
├── detail.go
└── main.go
├── 11-variable-promotion
├── 11-variable-promotion
└── main.go
├── 12-receiver-choose
├── addressable.go
└── main.go
├── 13-why-map-iteration-random
└── main.go
├── 14-kill-if
├── main.go
└── responsibility_chain.go
├── 15-table-drive-test
├── main.go
└── main_test.go
├── 16-fuzzy-test
├── main.go
└── main_test.go
├── 17-guard-clauses
└── main.go
├── 18-di-with-wire
├── main.go
├── wire.go
└── wire_gen.go
├── 19-json-struct-tip
├── tip0.go
├── tip1.go
├── tip2.go
├── tip3.go
└── tip4.go
├── 20-json-track
└── main.go
├── 21-functional-dao
├── go.mod
├── go.sum
├── main.go
└── model
│ ├── common.go
│ ├── mode_config.go
│ ├── model_functional.go
│ └── model_raw.go
├── 22-timewheel
├── main.go
└── txt
│ ├── raw.go
│ └── tw.go
├── 23-kill-err
├── killer
│ ├── nil-style.go
│ └── struct-style.go
└── main.go
├── 24-error-stack
├── errorSimple
│ ├── better-error.go
│ └── raw.go
└── main.go
├── 25-refactor-extract
├── extract.go
└── raw.go
├── go.mod
├── go.sum
└── readme.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | .vscode
3 | 00-playground
--------------------------------------------------------------------------------
/01-implement-interface-fast/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type someInterface interface {
4 | DoSomething()
5 | DoAnotherThing()
6 | }
7 |
8 | type someStruct struct {
9 | }
10 |
11 | // 如何快速实现接口 - how to quick impl someInterface for someStruct
12 |
13 | // 1. imple someInterface for someStruct{}
14 |
15 | // 2.
16 | var _ someInterface = (*someStruct)(nil)
17 |
18 | // DoAnotherThing implements someInterface
19 | func (*someStruct) DoAnotherThing() {
20 | panic("unimplemented")
21 | }
22 |
23 | // DoSomething implements someInterface
24 | func (*someStruct) DoSomething() {
25 | panic("unimplemented")
26 | }
27 |
--------------------------------------------------------------------------------
/02-variadic-functions-with-silce/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func ShowName(book ...string) {
4 | for _, content := range book {
5 | println(content)
6 | }
7 | }
8 |
9 | func main() {
10 | ShowName("rust", "go", "cpp")
11 | }
12 |
--------------------------------------------------------------------------------
/03-cast-interface/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type someStruct struct{}
4 |
5 | func NewPointer() *someStruct {
6 | var c *someStruct = nil
7 | return c
8 | }
9 |
10 | func main() {
11 | p := NewPointer()
12 | // 首 8 字节是否是 0 值
13 | if p == nil {
14 | println("p is nil")
15 | } else {
16 | println("p is not nil")
17 | }
18 | }
19 |
20 | /*
21 | # which output?
22 | # A. p is nil
23 | # B. p is not nil
24 | # C. panic
25 | */
26 |
27 | /*
28 |
29 | interface 结构体定义
30 |
31 | type iface struct {
32 | tab *itab
33 | data unsafe.Pointer
34 | }
35 |
36 | interface 一个 16 个字节的结构体,首 8 字节是类型字段,后 8 字节是数据指针。
37 |
38 | interface 变量新创建的时候是 nil ,则这 16 个字节是全 0 值;
39 |
40 | interface 变量的 nil 判断,汇编逻辑是判断首 8 字节是否是 0 值
41 |
42 | 函数 NewPointer 中p是一个具体类型指针。所以,它一定会把接口变量 iface (返回的接口)前 8 字节设置非零字段的,因为有具体类型呀(无论具体类型是否是 nil 指针)。
43 |
44 | 具体类型到接口的赋值一定会导致接口非零
45 | */
46 |
47 | /*
48 | In Go programming language, nil can refer to two things: a nil pointer or a nil interface value.
49 |
50 | A nil pointer is a special value of a pointer data type that indicates the pointer doesn't point to a valid memory address. When a pointer is declared but not initialized, it's zero value is nil.
51 |
52 | nil 指针占用的内存大小取决于指针数据类型,通常等于代码运行的平台上的内存地址的大小。
53 |
54 | nil 接口值由两部分组成:动态类型和动态值。动态类型部分是指向类型描述符的 nil 指针,该类型描述符描述了接口如果具有非 nil 值时存储的具体值的类型。动态值部分也是一个 nil 指针,表示没有具体值。因此,nil 接口值占用了两倍的内存地址大小。
55 |
56 | nil 指针和 nil 接口值的内存表示是不同的。前者占用的内存大小为一个内存地址的大小,而后者占用的内存大小为两个内存地址的大小。
57 | */
58 |
--------------------------------------------------------------------------------
/04-named-return/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 | a := []string{"go", "rust", "cpp"}
7 | b := CopyIt(a)
8 | fmt.Println(b)
9 | }
10 |
11 | func CopyIt(raw []string) (r []string) {
12 | // r = append(r, raw...)
13 | copy(r, raw)
14 | return r
15 | }
16 |
17 | /*
18 | how to optimize function CopyIt
19 | target: one line code
20 | */
21 |
--------------------------------------------------------------------------------
/05-options-pattern/config_way.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Book__ struct {
4 | cfg *Config
5 | }
6 | type Config struct {
7 | Title string
8 | ISBN string
9 | }
10 |
11 | func NewBook__(cfg *Config) *Book__ {
12 | return &Book__{cfg}
13 | }
14 |
15 | func main__() {
16 | _ = NewBook__(&Config{
17 | Title: "早点下班的go语言学习法",
18 | ISBN: "20230101",
19 | })
20 | }
21 |
--------------------------------------------------------------------------------
/05-options-pattern/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Book struct {
4 | Title string
5 | ISBN string
6 | }
7 |
8 | func NewBook(t string) *Book {
9 | return &Book{
10 | Title: t,
11 | ISBN: "",
12 | }
13 | }
14 |
15 | func NewBook2(t string, i string) *Book {
16 | return &Book{
17 | Title: t,
18 | ISBN: i,
19 | }
20 | }
21 |
22 | func main() {
23 | _ = NewBook("早点下班的go语言学习法")
24 | _ = NewBook2("早点下班的go语言学习法", "20230101")
25 | }
26 |
27 | // How To Make Constructor Better
28 |
--------------------------------------------------------------------------------
/05-options-pattern/options_way.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Book___ struct {
4 | Title string
5 | Code string
6 | }
7 | type Option func(*Book___)
8 |
9 | func NewBook_(options ...Option) *Book___ {
10 | b := &Book___{}
11 | for _, option := range options {
12 | option(b)
13 | }
14 | return b
15 | }
16 |
17 | func WithTitle(title string) Option {
18 | return func(b *Book___) {
19 | b.Title = title
20 | }
21 | }
22 |
23 | func WithISBN(ISBN string) Option {
24 | return func(b *Book___) {
25 | b.Code = ISBN
26 | }
27 | }
28 |
29 | func main_() {
30 | _ = NewBook_(
31 | WithTitle("Go for Dummies"), WithISBN("1234567890"),
32 | )
33 | }
34 |
--------------------------------------------------------------------------------
/06-range-expression/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 | s := []string{"something"}
7 | for i := 0; i < len(s); i++ {
8 | s = append(s, "new_one")
9 |
10 | fmt.Println(s)
11 | }
12 | }
13 |
14 | // Which Output is correct:
15 | // A. [something new_one]
16 |
17 | // B. [something new_one],
18 | // [something new_one new_one ]
19 | // [something new_one new_one new_one] ...
20 |
21 | // C. panic: runtime error: index out of range
22 |
--------------------------------------------------------------------------------
/07-map-trap/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 | m := map[string]int{
7 | "rust": 1,
8 | "go": 2,
9 | "cpp": 3,
10 | }
11 | //copy one
12 | m_copy := make(map[string]int, len(m))
13 | for k, v := range m {
14 | m_copy[k] = v
15 | }
16 |
17 | for k, v := range m_copy {
18 | m[k+"_new"] = v + 100
19 | }
20 |
21 | fmt.Printf("map length: %d\n", len(m))
22 | }
23 |
24 | // Output
25 | // A: 3
26 | // B: 6
27 | // C: I don't know
28 |
--------------------------------------------------------------------------------
/07-map-trap/map_struct.go.fake:
--------------------------------------------------------------------------------
1 | //fake code
2 |
3 | package main
4 |
5 | type Map struct {
6 | size int
7 | buckets []Bucket
8 | }
9 | type Bucket struct {
10 | key Key
11 | value Value
12 | }
13 |
14 | func (m *Map) Set(key Key, value Value) {
15 | // Compute the hash of the key.
16 | hash := hashFunction(key)
17 | // Determine the index of the bucket for this key.
18 | index := hash % m.size
19 | m.buckets[index] = Bucket{key: key, value: value}
20 | }
21 |
22 | func (m *Map) Get(key Key) (Value, bool) {
23 | hash := hashFunction(key)
24 | index := hash % m.size
25 | // Retrieve the value for the key from the bucket.
26 | if m.buckets[index].key == key {
27 | return m.buckets[index].value, true
28 | } else {
29 | return Value{}, false
30 | }
31 | }
32 |
33 | func hashFunction(key invalid type) {
34 | panic("unimplemented")
35 | }
36 |
--------------------------------------------------------------------------------
/08-map-struct-update/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | type Book struct {
6 | Title string
7 | Prince int
8 | }
9 |
10 | func main() {
11 | m := make(map[string]*Book)
12 | m["rust"] =
13 | &Book{Title: "rust", Prince: 20}
14 |
15 | //how to update prince?
16 | // m["rust"].Prince = 30
17 | b, ok := m["rust"]
18 | if ok {
19 | b.Prince = 30
20 | m["rust"] = b
21 |
22 | }
23 | // _ = &m["rust"]
24 |
25 | fmt.Printf(
26 | "prince: %d", m["rust"].Prince)
27 | }
28 |
--------------------------------------------------------------------------------
/09-no-class-Inheritance/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | type People struct {
6 | name string
7 | }
8 |
9 | func (p *People) getName() string {
10 | return p.name
11 | }
12 |
13 | type Namer interface {
14 | getName() string
15 | }
16 |
17 | func sayHello(p Namer) {
18 | fmt.Printf("Hello %s", p.getName())
19 | }
20 |
21 | type Boy struct {
22 | People
23 | }
24 |
25 | func main() {
26 | b := &Boy{
27 | People: People{name: "John"},
28 | }
29 | fmt.Printf("name is %s \n", b.name)
30 | fmt.Printf("hi to %s \n", b.getName())
31 |
32 | sayHello(b)
33 | }
34 |
--------------------------------------------------------------------------------
/10-interface-compare/detail.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "reflect"
4 |
5 | func main_() {
6 | var b = true
7 | var i interface{} = true
8 |
9 | if b == i {
10 | println("Comparable are equal")
11 | }
12 |
13 | if reflect.TypeOf(b) == reflect.TypeOf(i) {
14 | println("Dynamic types are equal")
15 | }
16 |
17 | if reflect.ValueOf(b) == reflect.ValueOf(i) {
18 | println("Dynamic values are equal")
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/10-interface-compare/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | pool := []interface{}{true}
5 | _ = pool[0]
6 | /*
7 | if tag {
8 | fmt.Println("This is a boolean value")
9 | }
10 | */
11 |
12 | /*
13 | //type assertion , get dynamic type of interface
14 | if tag.(bool) {
15 | fmt.Println("This is a boolean value")
16 | }
17 | */
18 |
19 | /*
20 | //https://go.dev/ref/spec#Comparison_operators
21 | if tag == true {
22 | fmt.Println("This is a boolean value")
23 | }
24 | */
25 | }
26 |
--------------------------------------------------------------------------------
/11-variable-promotion/11-variable-promotion:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Leizhenpeng/golang-trick/ba8ea88aca12363fb542d8625b17ab90547193ce/11-variable-promotion/11-variable-promotion
--------------------------------------------------------------------------------
/11-variable-promotion/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func getCounter() func() int {
6 | count := 0
7 | return func() int {
8 | count++
9 | return count
10 | }
11 | }
12 |
13 | func main() {
14 | counter := getCounter()
15 | fmt.Println(counter()) // 1
16 | fmt.Println(counter()) // 2
17 | fmt.Println(counter()) // 3
18 | }
19 |
20 | // 为何闭包会导致记忆效应?
21 | // 逃逸分析试试看
22 | // go build -gcflags='-m'
23 |
--------------------------------------------------------------------------------
/12-receiver-choose/addressable.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | type I interface{}
6 | type AInt int
7 | type AString string
8 |
9 | func explain1() {
10 | m := map[string]int{
11 | "go": 2023,
12 | }
13 | var i I = m["go"]
14 | fmt.Printf("i is of type %T\n", i)
15 | }
16 |
17 | func explain2() {
18 | var a AInt = 5
19 | var aPtr *AInt
20 |
21 | var i I = a
22 | fmt.Printf("i is of type %T\n", i)
23 | // aPtr = &(i.(AInt))
24 | var x AInt = i.(AInt)
25 | aPtr = &x
26 |
27 | var b AString = "hello"
28 | i = b
29 | fmt.Printf("i is of type %T, aPtr is of type %T\n", i, aPtr)
30 | }
31 |
32 | // can't take the address of value in interface
33 | // 1. some value is not addressable
34 | // 2. compiler will confilct with type
35 |
--------------------------------------------------------------------------------
/12-receiver-choose/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Circle struct {
4 | Radius float64
5 | }
6 |
7 | type Shaper interface {
8 | Area() float64
9 | Scale(factor float64)
10 | }
11 |
12 | func (c *Circle) Area() float64 {
13 |
14 | return 3.14 * c.Radius * c.Radius
15 | }
16 |
17 | func (c *Circle) Scale(factor float64) {
18 | c.Radius = c.Radius * factor
19 | }
20 |
21 | func main_() {
22 | // Common Sense:
23 | // 1.Value methods can be invoked on pointers and values,
24 | // 2. pointer methods can only be invoked on pointers.
25 | // When the value is addressable, you can winvoking a pointer method on a value by inserting the address operator automatically.
26 |
27 | // var c Shaper = Circle{10}
28 | var c Circle = Circle{10}
29 | // c.Scale(2)
30 | (&c).Scale(2)
31 | // c.Area()
32 | (&c).Area()
33 | }
34 |
--------------------------------------------------------------------------------
/13-why-map-iteration-random/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 | m := map[int]string{
7 | 1: "golang",
8 | 2: "rust",
9 | 3: "ts",
10 | }
11 |
12 | for k := range m {
13 | fmt.Println(k, m[k])
14 | }
15 |
16 | }
17 |
18 | // why map iteration is random?
19 | // 1. 汇编 go build -gcflags="-l -S -N" ./tip1.go > initMap.s 2>&1
20 | // 2. 查看核心调用代码 mapiterinit
21 | // 3. 查看 mapiterinit 源代码
22 |
--------------------------------------------------------------------------------
/14-kill-if/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type SellInfo struct {
4 | Price float64
5 | OrderCount int
6 | TotalCount int
7 | MemberShip int
8 | }
9 |
10 | func main2() {
11 | var a = SellInfo{
12 | Price: 1.0,
13 | OrderCount: 1,
14 | TotalCount: 20,
15 | MemberShip: 1,
16 | }
17 | if a.Price > 0 {
18 | println("true")
19 | }
20 | if a.TotalCount > a.OrderCount {
21 | println("true")
22 | } else {
23 | println("false")
24 | }
25 | if a.MemberShip == 1 {
26 | println("true")
27 | }
28 | if a.Price < 100 && a.MemberShip == 2 {
29 | println("true")
30 | }
31 | // if ...else...
32 | // if ...else if ...else if ...else if ...else...
33 | }
34 |
35 | // How To Kill Disgusting If Statement?
36 |
--------------------------------------------------------------------------------
/14-kill-if/responsibility_chain.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | type Rule interface {
6 | Check(sellInfo *SellInfo) bool
7 | }
8 |
9 | func Chain(sellInfo *SellInfo, rules ...Rule) bool {
10 | for _, r := range rules {
11 | if !r.Check(sellInfo) {
12 | return false
13 | }
14 | }
15 | return true
16 | }
17 |
18 | func main() {
19 | sellInfo := &SellInfo{
20 | Price: 1.0,
21 | OrderCount: 1,
22 | TotalCount: 20,
23 | MemberShip: 1,
24 | }
25 |
26 | rules := []Rule{
27 | &PriceRule{},
28 | &OrderCountRule{},
29 | &MemberShipRule{},
30 | &DiscountRule{},
31 | //...
32 |
33 | }
34 | r := Chain(sellInfo, rules...)
35 | fmt.Println(r)
36 |
37 | }
38 |
39 | type PriceRule struct{}
40 |
41 | func (pr *PriceRule) Check(sellInfo *SellInfo) bool {
42 | return sellInfo.Price > 0
43 | }
44 |
45 | type OrderCountRule struct{}
46 |
47 | func (ocr *OrderCountRule) Check(sellInfo *SellInfo) bool {
48 | return sellInfo.TotalCount > sellInfo.OrderCount
49 | }
50 |
51 | type MemberShipRule struct{}
52 |
53 | func (msr *MemberShipRule) Check(sellInfo *SellInfo) bool {
54 | return sellInfo.MemberShip == 1
55 | }
56 |
57 | type DiscountRule struct{}
58 |
59 | func (dr *DiscountRule) Check(sellInfo *SellInfo) bool {
60 | return sellInfo.Price < 100 && sellInfo.MemberShip == 2
61 | }
62 |
--------------------------------------------------------------------------------
/15-table-drive-test/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | // 222@qq.com --> 222, qq.com
4 | func SplitEmail(email string) (username, domain string) {
5 | atIndex := 0
6 | for i := 0; i < len(email); i++ {
7 | if email[i] == '@' {
8 | atIndex = i
9 | break
10 | }
11 | }
12 | username = email[:atIndex]
13 | domain = email[atIndex+1:]
14 | return
15 | }
16 |
--------------------------------------------------------------------------------
/15-table-drive-test/main_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/go-playground/assert/v2"
7 | "github.com/stretchr/testify/require"
8 | )
9 |
10 | //bad case
11 | func TestSplitEmail(t *testing.T) {
12 | username, domain := SplitEmail("1233@qq.com")
13 | require.NoError(t, nil)
14 | assert.Equal(t, username, "1233")
15 | assert.Equal(t, domain, "qq.com")
16 |
17 | username, domain = SplitEmail("22qqew@gami.com")
18 | require.NoError(t, nil)
19 | assert.Equal(t, username, "22qqew")
20 | assert.Equal(t, domain, "gami.com")
21 |
22 | username, domain = SplitEmail("123213@88.com")
23 | require.NoError(t, nil)
24 | assert.Equal(t, username, "123213")
25 | assert.Equal(t, domain, "88.com")
26 |
27 | username, domain = SplitEmail("123213@228.com")
28 | require.NoError(t, nil)
29 | assert.Equal(t, username, "123213")
30 | assert.Equal(t, domain, "228.com")
31 |
32 | }
33 |
34 | // table drive test
35 | func TestSplitEmail2(t *testing.T) {
36 | tests := []struct {
37 | name string
38 | email string
39 | username string
40 | domain string
41 | }{
42 | {
43 | name: "test 1",
44 | email: "22qqew@gami.com",
45 | username: "22qqew",
46 | domain: "gami.com",
47 | },
48 | {
49 | name: "test 2",
50 | email: "1233@qq.com",
51 | username: "1233",
52 | domain: "qq.com",
53 | },
54 | }
55 |
56 | for _, tt := range tests {
57 | t.Run(tt.name, func(t *testing.T) {
58 | username, domain := SplitEmail(tt.email)
59 | require.NoError(t, nil)
60 | assert.Equal(t, username, tt.username)
61 | assert.Equal(t, domain, tt.domain)
62 | })
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/16-fuzzy-test/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | // 222@qq.com --> 222, qq.com
4 | func EmailRead(email string) (username, domain string) {
5 | if email == "" {
6 | return
7 | }
8 | atIndex := 0
9 | for i := 0; i < len(email); i++ {
10 | if email[i] == '@' {
11 | atIndex = i
12 | break
13 | }
14 | }
15 | username = email[:atIndex]
16 | domain = email[atIndex+1:]
17 | return
18 | }
19 |
--------------------------------------------------------------------------------
/16-fuzzy-test/main_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "strings"
5 | "testing"
6 |
7 | "github.com/go-playground/assert"
8 | )
9 |
10 | func TestEmailRead(t *testing.T) {
11 | tests := []struct {
12 | name string
13 | email string
14 | wantU string
15 | wantD string
16 | }{
17 | {
18 | "test 1",
19 | "22@11.com",
20 | "22",
21 | "11.com",
22 | },
23 | {
24 | "test 2",
25 | "qwq@qq.com",
26 | "qwq",
27 | "qq.com",
28 | },
29 | }
30 | for _, tt := range tests {
31 | t.Run(tt.name, func(t *testing.T) {
32 | u, d := EmailRead(tt.email)
33 | if u != tt.wantU {
34 | t.Errorf("EmailRead() gotUsername = %v, want %v", u, tt.wantU)
35 | }
36 | if d != tt.wantD {
37 | t.Errorf("EmailRead() gotDomain = %v, want %v", d, tt.wantD)
38 | }
39 | })
40 | }
41 | }
42 |
43 | func FuzzTestEmailRead(f *testing.F) {
44 | testcases := []string{
45 | "222@11.com",
46 | "12313@qq.com",
47 | "22@222.com",
48 | }
49 | for _, tc := range testcases {
50 | f.Add(tc)
51 | }
52 | f.Fuzz(func(t *testing.T, email string) {
53 | username, domain :=
54 | EmailRead(email)
55 | if strings.Contains(email, "@") {
56 | assert.Equal(
57 | t, username+"@"+domain, email)
58 | }
59 | })
60 | }
61 |
--------------------------------------------------------------------------------
/17-guard-clauses/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | // nested if statements >> bad
4 | func winLottery(bmi float64) bool {
5 | if bmi >= 18.5 {
6 | if bmi <= 24.9 {
7 | return true
8 | } else {
9 | if bmi == 99.0 {
10 | return true
11 | } else {
12 | return false
13 | }
14 | }
15 | } else {
16 | return false
17 | }
18 | }
19 |
20 | // guard clauses >> good
21 | func winLottery_(bmi float64) bool {
22 | if bmi == 99.0 {
23 | return true
24 | }
25 | if bmi < 18.5 {
26 | return false
27 | }
28 | if bmi > 24.9 {
29 | return false
30 | }
31 | return true
32 | }
33 |
--------------------------------------------------------------------------------
/18-di-with-wire/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | type Meta struct{}
6 | type Message struct {
7 | msg string
8 | r Meta
9 | }
10 | type Mouth struct{ Message Message }
11 | type Hand struct{ Message Message }
12 | type People struct {
13 | Mouth Mouth
14 | Hand Hand
15 | //..
16 | }
17 |
18 | func main_() {
19 | meta := NewMeta()
20 | message := NewMessage("A Hi To U", meta)
21 | aMouth := NewMouth(message)
22 | aHand := NewHand(message)
23 | people := NewPeople(aMouth, aHand)
24 | people.Hi()
25 | }
26 |
27 | //use wire
28 | func main() {
29 | p := InitAPeople("Nice to meet you!")
30 | p.Hi()
31 | }
32 | func NewMeta() Meta {
33 | return Meta{}
34 | }
35 | func NewMessage(msg string, r Meta) Message {
36 | return Message{
37 | msg: msg,
38 | r: r,
39 | }
40 | }
41 | func NewMouth(m Message) Mouth {
42 | return Mouth{Message: m}
43 | }
44 | func NewHand(m Message) Hand {
45 | return Hand{Message: m}
46 | }
47 | func NewPeople(g Mouth, t Hand) People {
48 | return People{Mouth: g, Hand: t}
49 | }
50 |
51 | func (p People) Hi() {
52 | msg := p.Mouth.Greet()
53 | fmt.Println(msg)
54 | }
55 | func (g Mouth) Greet() string {
56 | return g.Message.msg
57 | }
58 |
--------------------------------------------------------------------------------
/18-di-with-wire/wire.go:
--------------------------------------------------------------------------------
1 | //go:build wireinject
2 | // +build wireinject
3 |
4 | package main
5 |
6 | import "github.com/google/wire"
7 |
8 | func InitAPeople(msg string) People {
9 | wire.Build(
10 | NewPeople,
11 | NewMouth,
12 | NewHand,
13 | //..
14 | NewMessage,
15 | NewMeta)
16 | return People{}
17 | }
18 |
--------------------------------------------------------------------------------
/18-di-with-wire/wire_gen.go:
--------------------------------------------------------------------------------
1 | // Code generated by Wire. DO NOT EDIT.
2 |
3 | //go:generate go run github.com/google/wire/cmd/wire
4 | //go:build !wireinject
5 | // +build !wireinject
6 |
7 | package main
8 |
9 | // Injectors from wire.go:
10 |
11 | func InitAPeople(msg string) People {
12 | meta := NewMeta()
13 | message := NewMessage(msg, meta)
14 | mouth := NewMouth(message)
15 | hand := NewHand(message)
16 | people := NewPeople(mouth, hand)
17 | return people
18 | }
19 |
--------------------------------------------------------------------------------
/19-json-struct-tip/tip0.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | )
7 |
8 | // Tip0:
9 | // Outer Struct Override Inner Struct
10 |
11 | type Inner struct {
12 | Name string `json:"name" `
13 | Price int `json:"price"`
14 | }
15 |
16 | type Outer struct {
17 | Inner
18 | Price string `json:"price"`
19 | }
20 |
21 | func main0() {
22 | m := new(Outer)
23 |
24 | m.Name = "river"
25 | m.Price = "100"
26 |
27 | r, _ := json.Marshal(m)
28 | fmt.Println(string(r))
29 | }
30 |
--------------------------------------------------------------------------------
/19-json-struct-tip/tip1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | )
7 |
8 | // Tip1: ignore field 2 ways:
9 |
10 | type Meta struct {
11 | Username string `json:"username" :"Username"`
12 | Password string `json:"password" :"Password"`
13 | // many more fields…
14 | }
15 |
16 | type MetaProxy struct {
17 | Meta
18 | // ignore by downCase
19 | score string `json:"score" :"Score"`
20 | // ignore by tag
21 | Password string `json:"-"`
22 | }
23 |
24 | func main1() {
25 | m := new(MetaProxy)
26 |
27 | m.Username = "river"
28 | m.score = "100"
29 | m.Password = "123456"
30 |
31 | r, _ := json.Marshal(m)
32 | fmt.Println(string(r))
33 | // {"username":"river",Password:""}
34 | }
35 |
--------------------------------------------------------------------------------
/19-json-struct-tip/tip2.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | )
7 |
8 | // Tip2: Omitting empty values
9 |
10 | type Resp struct {
11 | Code int `json:"code"`
12 | Msg string `json:"msg"`
13 | ErrorInf string `json:"error"`
14 | }
15 |
16 | type RespProxy struct {
17 | Resp
18 | ErrorInf string `json:"error,omitempty"`
19 | }
20 |
21 | func main2() {
22 | r := new(RespProxy)
23 | r.Code = 200
24 | r.Msg = "ok"
25 | r.ErrorInf = ""
26 |
27 | r2, _ := json.Marshal(r)
28 | fmt.Println(string(r2))
29 | // {"code":200,"msg":"ok"}
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/19-json-struct-tip/tip3.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | )
7 |
8 | // Tip3: type convert
9 |
10 | type Base struct {
11 | Code int `json:"code,string"`
12 | Msg string `json:"msg"`
13 | }
14 |
15 | func main3() {
16 | r := new(Base)
17 | r.Code = 200
18 | r.Msg = "ok"
19 |
20 | r2, _ := json.Marshal(r)
21 | fmt.Println(string(r2))
22 | // {"code":"200","msg":"ok"}
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/19-json-struct-tip/tip4.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | )
7 |
8 | // Tip4: Store Raw Data Without Type
9 |
10 | type Raw struct {
11 | Code int `json:"code"`
12 | Resp json.RawMessage `json:"resp"`
13 | }
14 |
15 | func main() {
16 | rawStr := `{
17 | "code":200,
18 | "resp":{"name":"river", "age":18}}`
19 | var raw Raw
20 | json.Unmarshal([]byte(rawStr), &raw)
21 | fmt.Println(string(raw.Resp))
22 | //{"name":"river", "age":18}
23 | }
24 |
--------------------------------------------------------------------------------
/20-json-track/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "time"
7 | )
8 |
9 | type Score struct {
10 | Name string
11 | time.Time
12 | }
13 |
14 | func main() {
15 | s := Score{
16 | Name: "river",
17 | Time: time.Now(),
18 | }
19 | r, err := json.Marshal(s)
20 | if err != nil {
21 | return
22 | }
23 | fmt.Printf("json: %s\n", string(r))
24 |
25 | /*
26 | guess the output
27 |
28 | Is { name: "river",
29 | time: "2023-02-21T23:04:34.042027+08:00"}
30 | ??
31 | */
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/21-functional-dao/go.mod:
--------------------------------------------------------------------------------
1 | module functional-dao
2 |
3 | go 1.18
4 |
5 | require gorm.io/driver/sqlite v1.4.4
6 |
7 | require (
8 | github.com/jinzhu/inflection v1.0.0 // indirect
9 | github.com/jinzhu/now v1.1.5 // indirect
10 | github.com/mattn/go-sqlite3 v1.14.15 // indirect
11 | gorm.io/gorm v1.24.0 // indirect
12 | )
13 |
--------------------------------------------------------------------------------
/21-functional-dao/go.sum:
--------------------------------------------------------------------------------
1 | github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
2 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
3 | github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
4 | github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
5 | github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
6 | github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
7 | github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
8 | gorm.io/driver/sqlite v1.4.4 h1:gIufGoR0dQzjkyqDyYSCvsYR6fba1Gw5YKDqKeChxFc=
9 | gorm.io/driver/sqlite v1.4.4/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI=
10 | gorm.io/gorm v1.24.0 h1:j/CoiSm6xpRpmzbFJsQHYj+I8bGYWLXVHeYEyyKlF74=
11 | gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
12 |
--------------------------------------------------------------------------------
/21-functional-dao/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "functional-dao/model"
4 |
5 | func main() {
6 | model.InitDB()
7 | model.AddFakeBook()
8 | model.ClientExample2()
9 | }
10 |
--------------------------------------------------------------------------------
/21-functional-dao/model/common.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "gorm.io/driver/sqlite"
5 | "gorm.io/gorm"
6 | )
7 |
8 | var db *gorm.DB
9 |
10 | type Book struct {
11 | gorm.Model
12 | ISBN int64 `json:"isbn"`
13 | Name string `json:"name"`
14 | Price int `json:"price"`
15 | Category int `json:"category"`
16 | }
17 |
18 | func ConnectDB() *gorm.DB {
19 | d, err := gorm.Open(sqlite.Open("file::memory:?cache=shared"), &gorm.Config{})
20 | if err != nil {
21 | panic("Failed to connect to database!")
22 | }
23 | db = d
24 | return nil
25 | }
26 |
27 | func GetDB() *gorm.DB {
28 | return db
29 | }
30 |
31 | func InitDB() {
32 | ConnectDB()
33 | db.AutoMigrate(&Book{})
34 | }
35 |
36 | func AddFakeBook() {
37 | db.Create(&Book{
38 | ISBN: 22,
39 | Name: "Book 22",
40 | Price: 100,
41 | Category: 2,
42 | })
43 |
44 | db.Create(&Book{
45 | ISBN: 23,
46 | Name: "Book 23",
47 | Price: 200,
48 | Category: 2,
49 | })
50 | }
51 |
--------------------------------------------------------------------------------
/21-functional-dao/model/mode_config.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import "fmt"
4 |
5 | type BookQuery struct {
6 | ID int
7 | ISBN int64
8 | Name string
9 | Price int
10 | }
11 |
12 | // Better 1: Struct + switch
13 | func FindBookByInfo2(info BookQuery) ([]Book, error) {
14 | var books []Book
15 | db := GetDB()
16 | switch {
17 | case info.ID != 0:
18 | db = db.Where("id = ?", info.ID)
19 | case info.ISBN != 0:
20 | db = db.Where("isbn = ?", info.ISBN)
21 | case info.Name != "":
22 | db = db.Where("name = ?", info.Name)
23 | case info.Price != 0:
24 | db = db.Where("price = ?", info.Price)
25 | }
26 | result := db.Find(&books)
27 | return books, result.Error
28 | }
29 |
30 | //client code
31 | func ClientExample1() {
32 |
33 | books, _ := FindBookByInfo2(BookQuery{
34 | ISBN: 22,
35 | })
36 | fmt.Printf("%+v", books)
37 | }
38 |
--------------------------------------------------------------------------------
/21-functional-dao/model/model_functional.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "fmt"
5 |
6 | "gorm.io/gorm"
7 | )
8 |
9 | type Option func(*gorm.DB) *gorm.DB
10 |
11 | func WithCategory(category int) Option {
12 | return func(db *gorm.DB) *gorm.DB {
13 | return db.Where("category = ?", category)
14 | }
15 | }
16 | func WithPrice(price int) Option {
17 | return func(db *gorm.DB) *gorm.DB {
18 | return db.Where("price = ?", price)
19 | }
20 | }
21 | func WithPriceRange(min, max int) Option {
22 | return func(db *gorm.DB) *gorm.DB {
23 | return db.Where("price >= ? AND price <= ?", min, max)
24 | }
25 | }
26 | func TableName(tableName string) Option {
27 | return func(db *gorm.DB) *gorm.DB {
28 | return db.Table(tableName)
29 | }
30 | }
31 |
32 | func GetBookByOptions(options ...Option) ([]Book, error) {
33 | var books []Book
34 | db := GetDB()
35 | for _, option := range options {
36 | db = option(db)
37 | }
38 | result := db.Find(&books)
39 | return books, result.Error
40 | }
41 |
42 | // client code
43 | func ClientExample2() {
44 | books, _ := GetBookByOptions(
45 | WithPrice(200),
46 | WithCategory(2),
47 | TableName("books"),
48 | //..
49 | )
50 | fmt.Println(books)
51 | }
52 |
--------------------------------------------------------------------------------
/21-functional-dao/model/model_raw.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import "fmt"
4 |
5 | func GetBookByID(id int) (Book, error) {
6 | var book Book
7 | db := GetDB()
8 | result := db.First(&book, id)
9 | return book, result.Error
10 | }
11 | func GetBookByISBN(isbn int64) (Book, error) {
12 | var book Book
13 | db := GetDB()
14 | result := db.Where("isbn = ?", isbn).First(&book)
15 | return book, result.Error
16 | }
17 | func GetBookByCategory(category int) ([]Book, error) {
18 | var books []Book
19 | db := GetDB()
20 | result := db.Where("category = ?", category).Find(&books)
21 | return books, result.Error
22 | }
23 | func GetBookByPrice(price int) ([]Book, error) {
24 | var books []Book
25 | db := GetDB()
26 | result := db.Where("price = ?", price).Find(&books)
27 | return books, result.Error
28 | }
29 |
30 | //client code
31 | func ClientExample() {
32 | r, _ := GetBookByPrice(200)
33 | fmt.Println(r)
34 | }
35 |
--------------------------------------------------------------------------------
/22-timewheel/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "goog/22-timewheel/txt"
6 | "runtime"
7 | "time"
8 |
9 | "github.com/rfyiamcool/go-timewheel"
10 | )
11 |
12 | func rawTest(count int, loop int) {
13 | var m1, m2 runtime.MemStats
14 | runtime.ReadMemStats(&m1)
15 | var totalTime time.Duration
16 | var totalMemory uint64
17 |
18 | for index := 0; index < loop; index++ {
19 | start := time.Now()
20 | for index := 0; index < count; index++ {
21 | txt.NewSMS("Hello, world!")
22 | }
23 |
24 | loopTime := time.Since(start)
25 | runtime.ReadMemStats(&m2)
26 | totalMemory += m2.TotalAlloc - m1.TotalAlloc
27 | m1 = m2
28 | totalTime += loopTime
29 | }
30 | fmt.Println("-----timer result-----")
31 | fmt.Printf("loop: %d , time : %v, memory : %dkb\n", 3, totalTime/3, totalMemory/3)
32 | }
33 |
34 | func twTest(count int, loop int) {
35 | tw, err :=
36 | timewheel.NewTimeWheel(1*time.Second, 20)
37 | if err != nil {
38 | panic(err)
39 | }
40 | tw.Start()
41 | defer tw.Stop()
42 | var m1, m2 runtime.MemStats
43 | runtime.ReadMemStats(&m1)
44 | var totalTime time.Duration
45 | var totalMemory uint64
46 |
47 | for index := 0; index < loop; index++ {
48 | start := time.Now()
49 | for index := 0; index < count; index++ {
50 | txt.NewSMS_("Hello, world!", tw)
51 | }
52 | loopTime := time.Since(start)
53 | runtime.ReadMemStats(&m2)
54 | totalMemory += m2.TotalAlloc - m1.TotalAlloc
55 | m1 = m2
56 | totalTime += loopTime
57 | }
58 | fmt.Println("-----timewheel result-----")
59 | fmt.Printf("loop: %d , time : %v, memory : %dkb\n", 3, totalTime/3, totalMemory/3)
60 | }
61 |
62 | func main() {
63 | rawTest(500000, 3)
64 | twTest(500000, 3)
65 | }
66 |
--------------------------------------------------------------------------------
/22-timewheel/txt/raw.go:
--------------------------------------------------------------------------------
1 | package txt
2 |
3 | import "time"
4 |
5 | type sms_raw struct {
6 | text string
7 | status string
8 | timer *time.Timer
9 | }
10 |
11 | func NewSMS(text string) *sms_raw {
12 | s := &sms_raw{
13 | text: text,
14 | status: "unverified",
15 | }
16 | s.timer = time.NewTimer(3 * time.Second)
17 | go func() {
18 | <-s.timer.C
19 | if s.status == "unverified" {
20 | s = nil
21 | }
22 | }()
23 | return s
24 | }
25 |
26 | func (s *sms_raw) Verify() {
27 | s.status = "verified"
28 | if !s.timer.Stop() && s.timer != nil {
29 | <-s.timer.C
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/22-timewheel/txt/tw.go:
--------------------------------------------------------------------------------
1 | package txt
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/rfyiamcool/go-timewheel"
7 | )
8 |
9 | type sms_tw struct {
10 | text string
11 | status string
12 | timer *timewheel.Task
13 | }
14 |
15 | /*
16 | tw, err := timewheel.NewTimeWheel(1*time.Second, 20)
17 | tw.Start()
18 | defer tw.Stop()
19 | */
20 | func NewSMS_(text string, tw *timewheel.TimeWheel) *sms_tw {
21 | s := &sms_tw{
22 | text: text,
23 | status: "unverified",
24 | }
25 | task := tw.Add(3*time.Second, func() {
26 | if s.status == "unverified" {
27 | s = nil
28 | }
29 | })
30 | s.timer = task
31 | return s
32 | }
33 |
34 | func (s *sms_tw) Verify_(tw *timewheel.TimeWheel) {
35 | s.status = "verified"
36 | tw.Remove(s.timer)
37 | }
38 |
--------------------------------------------------------------------------------
/23-kill-err/killer/nil-style.go:
--------------------------------------------------------------------------------
1 | package killer
2 |
3 | import "fmt"
4 |
5 | type Book struct {
6 | Name string
7 | Price int
8 | Store int
9 | Member int
10 | }
11 |
12 | func clientExample() {
13 | book := &Book{
14 | Name: "golang",
15 | Price: 100,
16 | Store: 100,
17 | Member: 1,
18 | }
19 | err := book.CalcDiscount(99)
20 | if err != nil {
21 | return
22 | }
23 | err = book.JuedeIfStore()
24 | if err != nil {
25 | return
26 | }
27 | err = book.IfSale()
28 | if err != nil {
29 | return
30 | }
31 | err = book.CalcTotal()
32 | if err != nil {
33 | return
34 | }
35 | fmt.Println("ok")
36 | }
37 | func (b *Book) CalcDiscount(count int) error {
38 | // ...
39 | b.Price = b.Price * count / 100
40 | err := SomeError()
41 | if err != nil {
42 | return err
43 | }
44 | return nil
45 | }
46 | func (b *Book) JuedeIfStore() error {
47 |
48 | return nil
49 | }
50 | func (b *Book) IfSale() error {
51 | // ...
52 | return nil
53 | }
54 | func (b *Book) CalcTotal() error {
55 | // ...
56 | return nil
57 | }
58 | func (b *Book) AddPay() error {
59 | // ...
60 | return nil
61 | }
62 | func (b *Book) AddOrder() error {
63 | // ...
64 | return nil
65 | }
66 |
67 | func SomeError() error {
68 | return fmt.Errorf("some error")
69 | }
70 |
--------------------------------------------------------------------------------
/23-kill-err/killer/struct-style.go:
--------------------------------------------------------------------------------
1 | package killer
2 |
3 | import "fmt"
4 |
5 | func someError() error {
6 | return fmt.Errorf("some error")
7 | }
8 |
9 | type Book2 struct {
10 | Name string
11 | Price int
12 | Store int
13 | Member int
14 | error error
15 | }
16 |
17 | func clientExample2() {
18 | book2 := &Book2{
19 | Name: "golang",
20 | Price: 100,
21 | Store: 100,
22 | Member: 1,
23 | }
24 | book2.CalcDiscount(99).
25 | JuedeIfStore().
26 | IfSale().
27 | AddPay().
28 | AddOrder().
29 | CheckAll()
30 |
31 | fmt.Println("ok")
32 |
33 | }
34 |
35 | func (b *Book2) CalcDiscount(count int) *Book2 {
36 | if b.error != nil {
37 | return b
38 | }
39 | // 具体的业务逻辑..
40 | b.error = someError()
41 | return b
42 | }
43 |
44 | func (b *Book2) JuedeIfStore() *Book2 {
45 | if b.error != nil {
46 | return b
47 | }
48 | // 具体的业务逻辑
49 | return b
50 | }
51 | func (b *Book2) IfSale() *Book2 {
52 | if b.error != nil {
53 | return b
54 | }
55 | return b
56 | }
57 | func (b *Book2) CalcTotal() *Book2 {
58 | if b.error != nil {
59 | return b
60 | }
61 | return b
62 | }
63 | func (b *Book2) AddPay() *Book2 {
64 | if b.error != nil {
65 | return b
66 | }
67 | return b
68 | }
69 | func (b *Book2) AddOrder() *Book2 {
70 | if b.error != nil {
71 | return b
72 | }
73 | return b
74 | }
75 |
76 | func (b *Book2) CheckAll() *Book2 {
77 | if b.error != nil {
78 | return b
79 | }
80 | return b
81 | }
82 |
--------------------------------------------------------------------------------
/23-kill-err/main.go:
--------------------------------------------------------------------------------
1 | package killerr
2 |
--------------------------------------------------------------------------------
/24-error-stack/errorSimple/better-error.go:
--------------------------------------------------------------------------------
1 | package errorSimple
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/pkg/errors"
7 | )
8 |
9 | func aFunc_() error {
10 | err := errors.New("A Mini Error !")
11 | if err != nil {
12 | return errors.Wrap(err, "aFunc_")
13 | }
14 | return nil
15 | }
16 |
17 | func anotherFunc_() error {
18 | err := aFunc_()
19 | if err != nil {
20 | return errors.Wrap(err, "anotherFunc_")
21 | }
22 | return nil
23 | }
24 |
25 | func StartClient2() {
26 | err := anotherFunc_()
27 | if err != nil {
28 | fmt.Printf("stack trace: %+v\n", errors.Cause(err))
29 | return
30 | }
31 | fmt.Println("ok")
32 | }
33 |
--------------------------------------------------------------------------------
/24-error-stack/errorSimple/raw.go:
--------------------------------------------------------------------------------
1 | package errorSimple
2 |
3 | import (
4 | "fmt"
5 | "github.com/pkg/errors"
6 | )
7 |
8 | func aFunc() error {
9 | err := errors.New("inFunc error")
10 | if err != nil {
11 | return errors.Wrap(err, "aFunc")
12 | }
13 | return nil
14 | }
15 |
16 | func anotherFunc() error {
17 | err := aFunc()
18 | if err != nil {
19 | return errors.Wrap(err, "anotherFunc")
20 | }
21 | return nil
22 | }
23 |
24 | func StartExample() {
25 | err := anotherFunc()
26 | if err != nil {
27 | fmt.Printf("main error: %+v\n",
28 | errors.Cause(err))
29 | return
30 | }
31 | fmt.Println("ok")
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/24-error-stack/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "goog/24-error-stack/errorSimple"
5 | )
6 |
7 | func main() {
8 | errorSimple.StartExample()
9 | // errorSimple.StartExample2()
10 | }
11 |
--------------------------------------------------------------------------------
/25-refactor-extract/extract.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "sync"
6 | )
7 |
8 | func (o Order) BasePrice() float64 {
9 | return o.Quantity * o.ItemPrice
10 | }
11 |
12 | func QuantityDiscount(o Order) float64 {
13 | discount := 0.0
14 | if o.Quantity > 500 {
15 | discount = 0.05
16 | }
17 | return o.BasePrice() * discount
18 | }
19 |
20 | func Shipping(o Order) float64 {
21 | shipping := o.BasePrice() * 0.1
22 | if shipping > 100 {
23 | shipping = 100
24 | }
25 | return shipping
26 | }
27 |
28 | func Price(o Order) float64 {
29 | return o.BasePrice() -
30 | QuantityDiscount(o) +
31 | Shipping(o)
32 | }
33 |
34 | func PriceQuick(o Order) float64 {
35 | var wg sync.WaitGroup
36 | var quantityDiscount float64
37 | var shipping float64
38 |
39 | basePrice := o.BasePrice()
40 | go func() {
41 | defer wg.Done()
42 | quantityDiscount = QuantityDiscount(o)
43 | }()
44 | go func() {
45 | defer wg.Done()
46 | shipping = Shipping(o)
47 | }()
48 | wg.Wait()
49 |
50 | return basePrice - quantityDiscount + shipping
51 | }
52 |
53 | func main_() {
54 | order := Order{Quantity: 600, ItemPrice: 10.0}
55 | fmt.Println(Price(order))
56 | }
57 |
--------------------------------------------------------------------------------
/25-refactor-extract/raw.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "math"
4 |
5 | type Order struct {
6 | Quantity float64
7 | ItemPrice float64
8 | }
9 |
10 | func price(order Order) float64 {
11 | // price formula:
12 | // base price - quantity discount + shipping
13 | return order.Quantity*order.ItemPrice -
14 | math.Max(0, order.Quantity-500)*order.ItemPrice*0.05 +
15 | math.Min(order.Quantity*order.ItemPrice*0.1, 100)
16 | }
17 |
18 | func main() {
19 | o := Order{
20 | Quantity: 1000,
21 | ItemPrice: 10,
22 | }
23 | println(price(o))
24 | }
25 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module goog
2 |
3 | go 1.18
4 |
5 | require (
6 | github.com/google/wire v0.5.0
7 | github.com/stretchr/testify v1.8.1
8 | )
9 |
10 | require (
11 | github.com/go-playground/assert v1.2.1
12 | github.com/rfyiamcool/go-timewheel v1.1.0
13 | )
14 |
15 | require (
16 | github.com/davecgh/go-spew v1.1.1 // indirect
17 | github.com/go-playground/assert/v2 v2.2.0
18 | github.com/pkg/errors v0.9.1
19 | github.com/pmezard/go-difflib v1.0.0 // indirect
20 | gopkg.in/yaml.v3 v3.0.1 // indirect
21 | )
22 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4 | github.com/go-playground/assert v1.2.1 h1:ad06XqC+TOv0nJWnbULSlh3ehp5uLuQEojZY5Tq8RgI=
5 | github.com/go-playground/assert v1.2.1/go.mod h1:Lgy+k19nOB/wQG/fVSQ7rra5qYugmytMQqvQ2dgjWn8=
6 | github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
7 | github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
8 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
9 | github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
10 | github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8=
11 | github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU=
12 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
13 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
14 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
15 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
16 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
17 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
18 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
19 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
20 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
21 | github.com/rfyiamcool/go-timewheel v1.1.0 h1:iEQb2pdDkiJEEFQf/ybDN3FKeje42qU+e3kXa2wbyHo=
22 | github.com/rfyiamcool/go-timewheel v1.1.0/go.mod h1:aKdsHKoLC2/Ax6WDNk2pOsNto6wlGahKBwBU3U0Jgnw=
23 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
24 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
25 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
26 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
27 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
28 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
29 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
30 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
31 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
32 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
33 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
34 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
35 | golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
36 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
37 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
38 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
39 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
40 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
41 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
42 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
43 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Golang Trick
2 |
3 | ## ❤️ Help other fellow developers
4 |
5 | Sharing is free but caring is priceless. [So, now please click here](https://www.bilibili.com/video/BV19e4y1A7DD/) and share this repository on Bilibili.
6 |
7 | ## Let's Gogogogogo!
8 |
9 | * **[如何让idea快速实现接口](https://www.bilibili.com/video/BV19e4y1A7DD)**
10 |
11 | _虽然go没有`imple`关键字,但依旧不影响gopher发挥._
12 | * **[可变参数`...`操作神奇切片](https://www.bilibili.com/video/BV1CA41167dM)**
13 |
14 | _神奇...的用法._
15 | * **[interface的nil陷阱](https://www.bilibili.com/video/BV1uT41197i4)**
16 |
17 | _golang的`interface{}`可没有ts的`any`省心._
18 | * **[给函数返回值起好名字](https://www.bilibili.com/video/BV1dT411D7vy)**
19 |
20 | _好的习惯会给无聊代码带来一阵清风._
21 |
22 | * **[你关注过range后面的表达式吗](https://www.bilibili.com/video/BV15Y411i7oz/)**
23 |
24 | _不同于for,range后接的表达式只会被求值一次._
25 |
26 | * **[永远不要在循环中更新map](https://www.bilibili.com/video/BV15Y411i7oz)**
27 |
28 | _map是动态数据类型,你根本不知道自己的数据存储在个桶._
29 |
30 | * **[如何更新map中的struct](https://www.bilibili.com/video/BV1ER4y1B7WY/)**
31 |
32 | _map如果存储struct作为value,不能修改它的字段._
33 |
34 | * **[如何优雅地实现继承](https://www.bilibili.com/video/BV1Ge4y1w7eL)**
35 |
36 | _结构体嵌套有用,但是并非处处有用._
37 |
38 | * **[方法接收器悖论](https://www.bilibili.com/video/BV1Ys4y1a7xy/)**
39 |
40 | _深刻摸索两种方法集和接口的关系._
41 |
42 | * **[闭包为何具有记忆效应](https://www.bilibili.com/video/BV14D4y1w7xP/)**
43 |
44 | _利用内存逃逸分析解释闭包的变量记忆._
45 |
46 | * **[责任链带你逃离IF地狱](https://www.bilibili.com/video/BV13A411U7Z4/)**
47 |
48 | _如果业务层条件判断过多,请选择责任链吧._
49 |
50 | * **[探索map循环随机的根本原因](https://www.bilibili.com/video/BV1JT411D7gB/)**
51 |
52 | _从golan源代码简单看看为何map是没有顺序的._
53 |
54 | * **[优雅的多用例单元测试](https://www.bilibili.com/video/BV1Wj411A7CN/)**
55 |
56 | _如果针对同一函数测试用例过多,试试表驱动测试._
57 |
58 | * **[利用Fuzz Test消灭BUG](https://www.bilibili.com/video/BV1KT411U7tF/)**
59 |
60 | _单元测试不放心,试试模糊测试._
61 |
62 | * **[利用卫语句消除IF嵌套](https://www.bilibili.com/video/BV18e4y1c7GW/)**
63 |
64 | _如果某些分支为异常层,就利用Guard Clause让他们提前返回吧._
65 |
66 | * **[如何漂亮地实现依赖注入](https://www.bilibili.com/video/BV1Db411X7dU/)**
67 |
68 | _复杂项目不要手动注入依赖,选择WIRE解放生产力._
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 | ---
79 |
80 | ## License
81 |
82 | Whole materials are licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
83 |
84 |
85 |
--------------------------------------------------------------------------------