0%

编译结果 结构体实现接口 结构体指针实现接口
结构体初始化变量 通过 不通过
结构体指针初始化变量 通过 通过
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package main

import "fmt"

// Ducker 鸭子类型
type Ducker interface {
Quack()
}

// Cat cat
type Cat struct{}

// Quack cat quack
func (c Cat) Quack() {
fmt.Println("cat quack")
}

// Dog dog
type Dog struct{}

// Quack dog duack
func (d *Dog) Quack() {
fmt.Println("dog quack")
}

func main() {
// 第一种情况,结构体初始化变量,结构体实现接口
var c1 Ducker = Cat{}
c1.Quack()

// 第二种情况,结构体指针初始化变量,结构体实现接口
var c2 Ducker = &Cat{}
c2.Quack()

// 第三种情况,结构体初始化变量,结构体指针实现接口
// compiler
// cannot use (Dog literal) (value of type Dog) as Ducker value in variable declaration:
// missing method Quack (Quack has pointer receiver)
// var d1 Ducker = Dog{}
// d1.Quack()

// 第四种情况,结构体指针初始化变量,结构体指针实现接口
var d2 Ducker = &Dog{}
d2.Quack()
}

  • 结构体指针初始化变量,结构体实现接口时,能够通过指针隐式地获取到指向的结构体
  • 结构体初始化变量,结构体指针实现接口时,变量缺少指针指向它

参考

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
// main.go
package main

import (
"fmt"
"log"

"github.com/pkg/errors"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)

// Settle 结算系统接口
type Settle interface {
GetSettleItem(id int) (*SettleItem, error)
GetSettleRule(id int) (*SettleRule, error)
}

// settle 结算系统接口实现
type settle struct {
db *gorm.DB
}

// SettleItem 结算项
type SettleItem struct {
ID int `gorm:"column:id"`
Name string `gorm:"column:name"`
}

// TableName 结算项表名
func (SettleItem) TableName() string {
return "t_settle_item"
}

// SettleRule 结算规则
type SettleRule struct {
ItemID int `gorm:"column:item_id"`
Month string `gorm:"column:month"`
Price float64 `gorm:"column:price"`
}

// TableName 结算规则表面
func (SettleRule) TableName() string {
return "t_settle_rule"
}

// NewSettle 创建实例
func NewSettle(db *gorm.DB) Settle {
return &settle{db: db}
}

// GetSettleItem 根据 ID 获取结算项
func (s *settle) GetSettleItem(id int) (*SettleItem, error) {
var i SettleItem
if err := s.db.First(&i, id).Error; err != nil {
return nil, errors.Wrap(err, "get settle item error")
}
return &i, nil
}

// GetSettleRule 根据结算项 ID 获取结算规则
func (s *settle) GetSettleRule(id int) (*SettleRule, error) {
var r SettleRule
if err := s.db.First(&r, "item_id = ?", id).Error; err != nil {
return nil, errors.Wrap(err, "get settle rule error")

}
return &r, nil
}

func main() {
dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
log.Fatal(err)
}
s := NewSettle(db)
item, err := s.GetSettleItem(1)
if err != nil {
log.Fatal(err)
}
fmt.Println(item)
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// main_test.go
package main

import (
"io/ioutil"
"log"
"os"
"testing"

"github.com/pkg/errors"
. "github.com/smartystreets/goconvey/convey"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)

const dbFile = "./test.db"

var db *gorm.DB

// TestMain 单测初始化
func TestMain(m *testing.M) {
if err := InitSQLite(); err != nil {
log.Fatal(err)
}
if err := ExecSQLite("test.sql"); err != nil {
log.Fatal(err)
}
code := m.Run()
if err := DelSQLite(); err != nil {
log.Fatal(err)
}
os.Exit(code)
}

// InitSQLite init sqlite
func InitSQLite() error {
if err := DelSQLite(); err != nil {
return err
}
var err error
db, err = gorm.Open(sqlite.Open(dbFile), &gorm.Config{})
if err != nil {
return errors.Wrap(err, "init sqlite error")
}
if err := db.AutoMigrate(&SettleItem{}); err != nil {
return errors.Wrap(err, "init sqlite error")
}
if err := db.AutoMigrate(&SettleRule{}); err != nil {
return errors.Wrap(err, "init sqlite error")
}
return nil
}

// ExecSQLite 根据 sql 文件执行 sql 语句
func ExecSQLite(sqlFile string) error {
data, err := ioutil.ReadFile(sqlFile)
if err != nil {
return errors.Wrap(err, "exec sqlite error")
}
s := string(data)
if err := db.Exec(s).Error; err != nil {
return errors.Wrap(err, "exec sqlite error")
}
return nil
}

// DelSQLite 删除 db 文件
func DelSQLite() error {
if _, err := os.Stat(dbFile); os.IsNotExist(err) {
return nil
}
err := os.Remove(dbFile)
if err != nil {
return errors.Wrap(err, "del sqlite error")
}
return nil
}

func TestNewSettle(t *testing.T) {
Convey("NewSettle", t, func() {
obj := NewSettle(db)
So(obj, ShouldNotBeNil)
})
}

func Test_settle_GetSettleItem(t *testing.T) {
Convey("GetSettleItem", t, func() {
obj := NewSettle(db)
i, err := obj.GetSettleItem(1)
So(err, ShouldBeNil)
So(i.Name, ShouldEqual, "1")
})
Convey("GetSettleItem not found", t, func() {
obj := NewSettle(db)
_, err := obj.GetSettleItem(2)
So(errors.Is(err, gorm.ErrRecordNotFound), ShouldEqual, true)
})
}

func Test_settle_GetSettleRule(t *testing.T) {
Convey("GetSettleRule", t, func() {
obj := NewSettle(db)
i, err := obj.GetSettleRule(1)
So(err, ShouldBeNil)
So(i.Price, ShouldEqual, 1)
})
Convey("GetSettleRule not found", t, func() {
obj := NewSettle(db)
_, err := obj.GetSettleRule(2)
So(errors.Is(err, gorm.ErrRecordNotFound), ShouldEqual, true)
})
}

1
2
INSERT INTO `t_settle_item` (`id`, `name`) VALUES (1, '1');
INSERT INTO `t_settle_rule` (`item_id`, `month`, `price`) VALUES (1, '202109', 1);

常见的行太长类型

  • 参数过多
  • 字符串太长
  • 判断条件太多
  • 链式调用太长
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
package main

import (
"fmt"

"gorm.io/driver/mysql"
"gorm.io/gorm"
)

func main() {
// argument too long
arg1 := "argument one"
arg2 := "argument two"
arg3 := "argument three"
arg4 := "argument four"
arg5 := "argument five"
arg6 := "argument six"
arg7 := "argument seven"
fmt.Println(arg1, arg2, arg3, arg4, arg5, arg6, arg7)
// fix
fmt.Println(
arg1,
arg2,
arg3,
arg4,
arg5,
arg6,
arg7,
)

// string too long
sql := "SELECT id, name, info FROM t_user user LEFT JOIN t_info info ON user.id = info.user_id where name != ''"
fmt.Println(sql)
// fix
sql = `
SELECT
id,
name,
info
FROM
t_user user
LEFT JOIN t_info info ON user.id = info.user_id
where
name != ''
and info != ''
`
fmt.Println(sql)
// or fix
sql = "SELECT id, name, info FROM t_user user " +
"LEFT JOIN t_info info ON user.id = info.user_id " +
"where name != '' and info != ''"
fmt.Println(sql)

// if condition too long
if arg1 == arg2 && arg2 == arg3 && arg3 == arg4 && arg4 == arg5 && arg5 == arg6 && arg6 == arg7 {
fmt.Println("pass")
}
// fix
if arg1 == arg2 &&
arg2 == arg3 &&
arg3 == arg4 &&
arg4 == arg5 &&
arg5 == arg6 &&
arg6 == arg7 {
fmt.Println("pass")
}

// function chain call too long
db, err := gorm.Open(mysql.Open(""), &gorm.Config{})
if err != nil {
fmt.Println(err)
return
}
db.Table("table").Select("id", "name", "info").Where("name != ? and info != ?", "", "").Find(nil)
// fix
db.Table("table").
Select("id", "name", "info").
Where("name != ? and info != ?", "", "").
Find(nil)
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
go run main.go
argument one argument two argument three argument four argument five argument six argument seven
argument one argument two argument three argument four argument five argument six argument seven
SELECT id, name, info FROM t_user user LEFT JOIN t_info info ON user.id = info.user_id where name != ''

SELECT
id,
name,
info
FROM
t_user user
LEFT JOIN t_info info ON user.id = info.user_id
where
name != ''
and info != ''

SELECT id, name, info FROM t_user user LEFT JOIN t_info info ON user.id = info.user_id where name != '' and info != ''

2021/10/08 19:21:11 /Users/holtchen/Desktop/go/main.go:69
[error] failed to initialize database, got error dial tcp 127.0.0.1:3306: connect: connection refused
dial tcp 127.0.0.1:3306: connect: connection refused

template.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
// License

// Package doc 包内容描述
//
// Heading 标题一
//
// 描述一
//
// Heading 标题二
//
// 描述二
//
// Code Blocks
//
// 代码块
// fmt.Printf("Hello, World")
//
// Links
//
// 链接
//
// https://www.google.com
package doc

import "fmt"

// ConstOne 常量一
const ConstOne = "const one"

// ConstTwo 常量二
const ConstTwo = "const two"

// Const 集合
const (
ConstThree = "const three"
ConstFour = "const four"
)

// VarOne 全局变量一
var VarOne = "var one"

// VarTwo 全局变量二
var VarTwo = "var two"

// Var 变量集合
var (
VarThree = "var three"
VarFour = "var four"
)

// Namer 获取名字
type Namer interface {
GetName() string
}

// FunctionOne 函数一
func FunctionOne() {
fmt.Println("function one")
}

// FunctionTwo 函数二
func FunctionTwo() {
fmt.Println("function two")
}

// StructOne 结构体一
type StructOne struct{}

// NewStructOne 创建结构体一实例
func NewStructOne() *StructOne {
return &StructOne{}
}

// GetName 返回结构体名称
func (StructOne) GetName() string {
return "StructOne"
}

// StructTwo 结构体二
type StructTwo struct{}

example_test.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package doc

import "fmt"

// Example 包示例一
func Example_one() {
fmt.Println("example one")

// Output:
// example one
}

// Example 包示例二
func Example_two() {
fmt.Println("example two")

// Output:
// example two
}

// ExampleNamer 接口示例
func ExampleNamer() {
fmt.Println("namer")
}

// ExampleStructOne 结构体一示例
func ExampleStructOne() {
fmt.Println("const one")
}

// ExampleNewStructOne 函数示例
func ExampleNewStructOne() {
fmt.Println("new struct one")
}

// ExampleStructOne_GetName 方法示例
func ExampleStructOne_GetName() {
fmt.Println("get name")
}

参考

https://elliotchance.medium.com/godoc-tips-tricks-cda6571549b

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package main

import (
"errors"
"fmt"
)

// HTTPError http error struct
type HTTPError struct {
Status int
Msg string
}

func (h *HTTPError) Error() string {
return fmt.Sprintf("status: %d Message: %s", h.Status, h.Msg)
}

// NewHTTPError new http error
func NewHTTPError(status int, msg string) *HTTPError {
return &HTTPError{
Status: status,
Msg: msg,
}
}

// HTTP error class
var (
ErrBadRequest = NewHTTPError(400, "Bad Request")
ErrGatewayTimeout = NewHTTPError(503, "Gateway Timeout")
)

// API struct
type API struct {
}

// NewAPI new api
func NewAPI() *API {
return &API{}
}

// Get api get
func (a *API) Get() error {
return fmt.Errorf("method get error: %w", ErrBadRequest)
}

// Post api post
func (a *API) Post() error {
return fmt.Errorf("method post error: %w", ErrGatewayTimeout)
}

func main() {
api := NewAPI()
err := api.Get()
fmt.Println(err)
// check http error
var httpError *HTTPError
fmt.Println(errors.As(err, &httpError))
// check bad request
fmt.Println(errors.Is(err, ErrBadRequest))
// unwrap
fmt.Println(errors.Unwrap(err))
}

参考

template.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
// License

// Package doc 包内容描述
//
// Heading 标题一
//
// 描述一
//
// Heading 标题二
//
// 描述二
//
// Code Blocks
//
// 代码块
// fmt.Printf("Hello, World")
//
// Links
//
// 链接
//
// https://www.google.com
package doc

import "fmt"

// ConstOne 常量一
const ConstOne = "const one"

// ConstTwo 常量二
const ConstTwo = "const two"

// Const 集合
const (
ConstThree = "const three"
ConstFour = "const four"
)

// VarOne 全局变量一
var VarOne = "var one"

// VarTwo 全局变量二
var VarTwo = "var two"

// Var 变量集合
var (
VarThree = "var three"
VarFour = "var four"
)

// Namer 获取名字
type Namer interface {
GetName() string
}

// FunctionOne 函数一
func FunctionOne() {
fmt.Println("function one")
}

// FunctionTwo 函数二
func FunctionTwo() {
fmt.Println("function two")
}

// StructOne 结构体一
type StructOne struct{}

// NewStructOne 创建结构体一实例
func NewStructOne() *StructOne {
return &StructOne{}
}

// GetName 返回结构体名称
func (StructOne) GetName() string {
return "StructOne"
}

// StructTwo 结构体二
type StructTwo struct{}

example_test.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package doc

import "fmt"

// Example 包示例一
func Example_one() {
fmt.Println("example one")

// Output:
// example one
}

// Example 包示例二
func Example_two() {
fmt.Println("example two")

// Output:
// example two
}

// ExampleNamer 接口示例
func ExampleNamer() {
fmt.Println("namer")
}

// ExampleStructOne 结构体一示例
func ExampleStructOne() {
fmt.Println("const one")
}

// ExampleNewStructOne 函数示例
func ExampleNewStructOne() {
fmt.Println("new struct one")
}

// ExampleStructOne_GetName 方法示例
func ExampleStructOne_GetName() {
fmt.Println("get name")
}

参考

https://elliotchance.medium.com/godoc-tips-tricks-cda6571549b

文本协议

  1. 透明性
  • 无需专门工具就可以很容易地读写和编辑文本流
  1. 可扩展性
  • 二进制格式通常指定了给定值的分配位数,要扩展位数非常困难
  1. 互用性
  2. 存储/事务处理的经济性