Go-20-编程模式

深度比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import (
"fmt"
"reflect"
)

func main() {

v1 := data{}
v2 := data{}
fmt.Println("v1 == v2:",reflect.DeepEqual(v1,v2))
//prints: v1 == v2: true

m1 := map[string]string{"one": "a","two": "b"}
m2 := map[string]string{"two": "b", "one": "a"}
fmt.Println("m1 == m2:",reflect.DeepEqual(m1, m2))
//prints: m1 == m2: true

s1 := []int{1, 2, 3}
s2 := []int{1, 2, 3}
fmt.Println("s1 == s2:",reflect.DeepEqual(s1, s2))
//prints: s1 == s2: true
}

接口编程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
type Shape interface {
Sides() int
Area() int
}
type Square struct {
len int
}
func (s* Square) Sides() int {
return 4
}
func main() {
s := Square{len: 5}
fmt.Printf("%d\n",s.Sides())
}


var _ Shape = (*Square)(nil) //接口实现校验

声明一个 _ 变量(没人用)会把一个 nil 的空指针从 Square 转成 Shape,这样,如果没有实现完相关的接口方法,编译器就会报错:

cannot use (*Square)(nil) (type *Square) as type Shape in assignment: *Square does not implement Shape (missing Area method)

性能

  • 如果需要把数字转换成字符串,使用 strconv.Itoa()fmt.Sprintf() 要快一倍左右。
  • 尽可能避免把String转成[]Byte ,这个转换会导致性能下降。
  • 如果在 for-loop 里对某个 Slice 使用 append(),请先把 Slice 的容量扩充到位,这样可以避免内存重新分配以及系统自动按 2 的 N 次方幂进行扩展但又用不到的情况,从而避免浪费内存。
  • 使用StringBuffer 或是StringBuild 来拼接字符串,性能会比使用 + 或 +=高三到四个数量级。
  • 尽可能使用并发的 goroutine,然后使用 sync.WaitGroup 来同步分片操作。
  • 避免在热代码中进行内存分配,这样会导致 gc 很忙。尽可能使用 sync.Pool 来重用对象。
  • 使用 lock-free 的操作,避免使用 mutex,尽可能使用 sync/Atomic包(关于无锁编程的相关话题,可参看《无锁队列实现》或《无锁 Hashmap 实现》)。
  • 使用 I/O 缓冲,I/O 是个非常非常慢的操作,使用 bufio.NewWrite() 和 bufio.NewReader() 可以带来更高的性能。
  • 对于在 for-loop 里的固定的正则表达式,一定要使用 regexp.Compile() 编译正则表达式。性能会提升两个数量级。
  • 需要更高性能的协议,就要考虑使用 protobuf 或 msgp 而不是 JSON,因为 JSON 的序列化和反序列化里使用了反射。
  • 使用 Map 的时候,使用整型的 key 会比字符串的要快,因为整型比较比字符串比较要快

错误检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func parse(r io.Reader) (*Point, error) {

var p Point

if err := binary.Read(r, binary.BigEndian, &p.Longitude); err != nil {
return nil, err
}
if err := binary.Read(r, binary.BigEndian, &p.Latitude); err != nil {
return nil, err
}
if err := binary.Read(r, binary.BigEndian, &p.Distance); err != nil {
return nil, err
}
if err := binary.Read(r, binary.BigEndian, &p.ElevationGain); err != nil {
return nil, err
}
if err := binary.Read(r, binary.BigEndian, &p.ElevationLoss); err != nil {
return nil, err
}
}

太多的 if err != nil {}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

func parse(r io.Reader) (*Point, error) {
var p Point
var err error
read := func(data interface{}) {
if err != nil { //如果失败则不执行
return
}
err = binary.Read(r, binary.BigEndian, data)
}

read(&p.Longitude)
read(&p.Latitude)
read(&p.Distance)
read(&p.ElevationGain)
read(&p.ElevationLoss)

if err != nil {
return &p, err
}
return &p, nil
}

上述代码存在一个变量err和一个内部函数 read

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
type Reader struct {
r io.Reader
err error
}

func (r *Reader) read(data interface{}) {
if r.err == nil {
r.err = binary.Read(r.r, binary.BigEndian, data)
}
}


func parse(input io.Reader) (*Point, error) {
var p Point
r := Reader{r: input}

r.read(&p.Longitude)
r.read(&p.Latitude)
r.read(&p.Distance)
r.read(&p.ElevationGain)
r.read(&p.ElevationLoss)

if r.err != nil {
return nil, r.err
}

return &p, nil
}

主要应用于相同业务处理场景

错误包装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import "github.com/pkg/errors"	//第三方库

//错误包装
if err != nil {
return errors.Wrap(err, "read failed")
}

// Cause接口
switch err := errors.Cause(err).(type) {
case *MyError:
// handle specifically
default:
// unknown error
}

多参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func main() {
slice1 := make([]interface{}, 0)
slice2 := make([]interface{}, 1) //不要这样使用 会初始化1的nil
slice1 = append(slice1, 1, "2")
slice2 = append(slice2, 1, "2")
test(slice1) //args [[1 2]] num:1
test(slice1...) //args [1 2] num:2
test(slice2) //args [[<nil> 1 2]] num:1
test(slice2...) //args [<nil> 1 2] num:3
}

func test(args ...interface{}) {
fmt.Printf("args %v num:%d \n", args, len(args))
}

Map-Reduce

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
func main() {
var list = []string{"Hao", "Chen", "MegaEase"}

x := Reduce(list, func(s string) int {
return len(s)
})

fmt.Printf("%v\n", x) //15

var intset = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
out := Filter(intset, func(n int) bool {
return n%2 == 1
})
fmt.Printf("%v\n", out) //[1 3 5 7 9]

out = Filter(intset, func(n int) bool {
return n > 5
})
fmt.Printf("%v\n", out) //[6 7 8 9 10]
}

func Reduce(arr []string, fn func(s string) int) int {
sum := 0
for _, it := range arr {
sum += fn(it)
}
return sum
}

func Filter(arr []int, fn func(n int) bool) []int {
var newArray = []int{}
for _, it := range arr {
if fn(it) {
newArray = append(newArray, it)
}
}
return newArray
}

健壮的泛型Map

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
func verifyFuncSignature(fn reflect.Value, types ...reflect.Type) bool {

//Check it is a funciton
if fn.Kind() != reflect.Func {
return false
}
// NumIn() - returns a function type's input parameter count.
// NumOut() - returns a function type's output parameter count.
if (fn.Type().NumIn() != len(types)-1) || (fn.Type().NumOut() != 1) {
return false
}
// In() - returns the type of a function type's i'th input parameter.
for i := 0; i < len(types)-1; i++ {
if fn.Type().In(i) != types[i] {
return false
}
}
// Out() - returns the type of a function type's i'th output parameter.
outType := types[len(types)-1]
if outType != nil && fn.Type().Out(0) != outType {
return false
}
return true
}

健壮的泛型Reduce

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

func Reduce(slice, pairFunc, zero interface{}) interface{} {
sliceInType := reflect.ValueOf(slice)
if sliceInType.Kind() != reflect.Slice {
panic("reduce: wrong type, not slice")
}

len := sliceInType.Len()
if len == 0 {
return zero
} else if len == 1 {
return sliceInType.Index(0)
}

elemType := sliceInType.Type().Elem()
fn := reflect.ValueOf(pairFunc)
if !verifyFuncSignature(fn, elemType, elemType, elemType) {
t := elemType.String()
panic("reduce: function must be of type func(" + t + ", " + t + ") " + t)
}

var ins [2]reflect.Value
ins[0] = sliceInType.Index(0)
ins[1] = sliceInType.Index(1)
out := fn.Call(ins[:])[0]

for i := 2; i < len; i++ {
ins[0] = out
ins[1] = sliceInType.Index(i)
out = fn.Call(ins[:])[0]
}
return out.Interface()
}

健壮的泛型 Filter

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
func Filter(slice, function interface{}) interface{} {
result, _ := filter(slice, function, false)
return result
}

func FilterInPlace(slicePtr, function interface{}) {
in := reflect.ValueOf(slicePtr)
if in.Kind() != reflect.Ptr {
panic("FilterInPlace: wrong type, " +
"not a pointer to slice")
}
_, n := filter(in.Elem().Interface(), function, true)
in.Elem().SetLen(n)
}

var boolType = reflect.ValueOf(true).Type()

func filter(slice, function interface{}, inPlace bool) (interface{}, int) {

sliceInType := reflect.ValueOf(slice)
if sliceInType.Kind() != reflect.Slice {
panic("filter: wrong type, not a slice")
}

fn := reflect.ValueOf(function)
elemType := sliceInType.Type().Elem()
if !verifyFuncSignature(fn, elemType, boolType) {
panic("filter: function must be of type func(" + elemType.String() + ") bool")
}

var which []int
for i := 0; i < sliceInType.Len(); i++ {
if fn.Call([]reflect.Value{sliceInType.Index(i)})[0].Bool() {
which = append(which, i)
}
}

out := sliceInType

if !inPlace {
out = reflect.MakeSlice(sliceInType.Type(), len(which), len(which))
}
for i := range which {
out.Index(i).Set(sliceInType.Index(which[i]))
}

return out.Interface(), len(which)
}

反射不适用于高性能的地方

类型检查

Type Assert

对变量进行 .(type)的转型操作返回两个值,分别是 variable 和 error

variable 是被转换好的类型,error 表示如果不能转换类型,则会报错

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
//Container is a generic container, accepting anything.
type Container []interface{}

//Put adds an element to the container.
func (c *Container) Put(elem interface{}) {
*c = append(*c, elem)
}
//Get gets an element from the container.
func (c *Container) Get() interface{} {
elem := (*c)[0]
*c = (*c)[1:]
return elem
}

intContainer := &Container{}
intContainer.Put(7)
intContainer.Put(42)

// assert that the actual type is int
elem, ok := intContainer.Get().(int)
if !ok {
fmt.Println("Unable to read an int from intContainer")
}

fmt.Printf("assertExample: %d (%T)\n", elem, elem)

反射

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
type Container struct {
s reflect.Value
}
func NewContainer(t reflect.Type, size int) *Container {
if size <=0 { size=64 }
return &Container{
s: reflect.MakeSlice(reflect.SliceOf(t), 0, size),
}
}
func (c *Container) Put(val interface{}) error {
if reflect.ValueOf(val).Type() != c.s.Type().Elem() {
return fmt.Errorf(“Put: cannot put a %T into a slice of %s",
val, c.s.Type().Elem()))
}
c.s = reflect.Append(c.s, reflect.ValueOf(val))
return nil
}
func (c *Container) Get(refval interface{}) error {
if reflect.ValueOf(refval).Kind() != reflect.Ptr ||
reflect.ValueOf(refval).Elem().Type() != c.s.Type().Elem() {
return fmt.Errorf("Get: needs *%s but got %T", c.s.Type().Elem(), refval)
}
reflect.ValueOf(refval).Elem().Set( c.s.Index(0) )
c.s = c.s.Slice(1, c.s.Len())
return nil
}

参考文献

  1. https://coolshell.cn/articles/21128.html
  2. https://github.com/robpike