This article will continue to update more common Golang tips, with a variety of tips to open up ideas;

0. Recent Update (June 03, 2021)

  • Chain calls
  • ErrorResult Multi-value encapsulation
  • JsonResult repackaging

1. Singleton mode

  • It is used to instantiate only once, such as the configuration module and log module.
package main

import (
	"fmt"
	"sync"
)

var cc *WebConfig
var once sync.Once

type WebConfig struct {
	Port int `json:"port"`
}

func GetConfig(a) *WebConfig  {
	once.Do(func(a) {
		cc = &WebConfig{Port: 8080}})return cc
}

func main(a)  {
	cfg1 := GetConfig()
	cfg2 := GetConfig()
	fmt.Println(cfg1 == cfg2)
}

Copy the code

2. The function executes timeout control


package main

import (
	"fmt"
	"time"
)
//1. Put business processes into coroutines
// 2. Insert the service result into channel
func job(a) chan string{
	ret:=make(chan string)
	go func(a) {
		time.Sleep(time.Second*2)
		ret<- "success"} ()return ret
}
func run(a) (interface{},  error) {
	c:=job()
	select {
	case r:=<-c:
		return r,nil
	case <-time.After(time.Second*3) :return nil,fmt.Errorf("time out")}}func main(a)  {
	fmt.Println(run())
}

Copy the code

3. Reflection example

  • Principal clarityreflect.Kindreflect.ValueThe use of the
package main

import (
	"fmt"
	"reflect"
)

type User struct {
	UserId int `name:"uid" bcd:"3456"`
	UserName string
}
func Map2Struct(m map[string]interface{},u interface{})  {
	v:=reflect.ValueOf(u)
	if v.Kind()==reflect.Ptr{
		v=v.Elem()
		ifv.Kind()! =reflect.Struct{panic("must struct")
		}
		findFromMap:= func(key string,nameTag string ) interface{} {for k,v:=range m{
				if k==key || k==nameTag {
					return v
				}
			}
			return nil
		}
		for i:=0; i<v.NumField(); i++{ get_value:=findFromMap(v.Type().Field(i).Name,v.Type().Field(i).Tag.Get("name"))
			ifget_value! =nil && reflect.ValueOf(get_value).Kind()==v.Field(i).Kind(){
				v.Field(i).Set(reflect.ValueOf(get_value))
			}
		}
	}else{
		panic("must ptr")}}func main(a)  {
	u:=&User{}
	m:=map[string]interface{} {"id":123."uid":101."UserName":"shenyi"."age":19,
	}
	Map2Struct(m,u)
	fmt.Printf("%+v", u)
}
Copy the code

4. Constructor tips

  • The purpose is to improve code maintainability and readability, but there is a relative performance penalty
package main

import (
	"fmt"
)

type UserAttrFunc func(*User)/ / setUserProperty of the function type
type UserAttrFuncs []UserAttrFunc
func(this UserAttrFuncs) apply(u *User)  {
	for _,f:=range this{
		f(u)
	}
}
func WithUserID(id int) UserAttrFunc  {
	return func(u *User) {
		u.Id=id
	}
}
func WithUserName(name string) UserAttrFunc  {
	return func(u *User) {
		u.Name=name
	}
}

type User struct {
	Id int
	Name string
	Sex byte
}
// Optionally assign to ID
func NewUser(fs ... UserAttrFunc) *User  {
	u:= new(User)
	UserAttrFuncs(fs).apply(u)
	return u
}

func main(a)  {
	u:=NewUser(
		WithUserName("shenyi"),
		WithUserID(105),
	)
	fmt.Println(u)

}
Copy the code

5. Simple Factory mode

package main

import "fmt"

type UserType int 

type User interface {
	GetRole() string
}
type Member struct {}
func(this *Member) GetRole(a) string {
	return   "Member User"
}
type Admin struct {}
func(this *Admin) GetRole(a) string {
	return  "Background Admin User"
}

// Enumeration type
const (
	Mem UserType = iota
	Adm
)
func CreateUser(t UserType) User{
	switch t {
	case Mem:
		return new(Member)
	case Adm:
		return new(Admin)
	default:
		return new(Member)
	}
}

func main(a)  {
	fmt.Println(CreateUser(Adm).GetRole())
}
Copy the code

6. Abstract factory pattern

  • Abstract factory mode is also a high maintainability and expansibility design mode under polymorphic condition, which conforms to the design idea that high-level code should depend on abstraction rather than the implementation of concrete modules.
package main

import "fmt"

/ / entity
type User interface {
	GetRole() string
}
type Member struct {}
func(this *Member) GetRole(a) string {
	return   "Member User"
}
type Admin struct {}
func(this *Admin) GetRole(a) string {
	return  "Background Admin User"
}
const (
	Mem=iota
	Adm
)

/ / abstract
type AbstractFactory interface {
	CreateUser() User
}
type MemberFactory struct {}
func(this *MemberFactory) CreateUser(a) User{
	return &Member{}
}
type AdminFactory struct {}
func(this *AdminFactory) CreateUser(a) User{
	return &Admin{}
}

func main(a)  {
	var fact AbstractFactory=new(AdminFactory)
	fmt.Println(fact.CreateUser().GetRole())
}
Copy the code

7. Decorator mode

  • Decorator is a design pattern that embodies AOP ideas. Adding new features without changing the original method;
package main

import "net/http"


func CheckLogin(f http.HandlerFunc) http.HandlerFunc{
	return func(writer http.ResponseWriter, request *http.Request) {
		 if request.URL.Query().Get("token") = =""{
			 writer.Write([]byte("token error"))}else{
			 f(writer,request)
		 }

	}
}
func index(writer http.ResponseWriter, request *http.Request) {
	writer.Write([]byte("index"))}func main(a)  {

	 http.HandleFunc("/",CheckLogin(index) )
	 http.ListenAndServe(": 8080".nil)}Copy the code

8. Simple prefix trees

  • It is used to match routes in the routing framework
package main

import "fmt"

type Node struct {
    isend bool  // The last one
    Children map[string]*Node  // Use map to save traversal
}
func NewNode(a) *Node {
    return &Node{Children:make(map[string]*Node)}
}

type Trie struct {
    root *Node
}
func NewTrie(a) *Trie {
    return &Trie{root:NewNode()}
}

func(this *Trie) Insert(str string ){
    // The first node must be empty
    current:=this.root
    for _,item:=range ([]rune)(str){
        if _,ok:=current.Children[string(item)]; ! ok{ current.Children[string(item)]=NewNode()
        }
        current=current.Children[string(item)]
    }
    current.isend=true   // The last one
}

func(this *Trie) Search(str string ) bool{
    current:=this.root
    for _,item:=range ([]rune)(str){
        if _,ok:=current.Children[string(item)]; ! ok{return  false
        }
        current=current.Children[string(item)]
    }
    return current.isend    // The last one
}


func test(a)  {
    strs:=[]string{"go"."gin"."golang"."goapp"."guest"}
    tree:=NewTrie()
    for _,s:=range strs{
        tree.Insert(s)
    }

    // None of them match
    strs=append(strs,"gi"."gogo"."gia")
    for _,s:=range strs{
        fmt.Println(tree.Search(s))
    }

}
func main(a)  {

    test()
}

Copy the code

9. Line reads file or string

package main

import (
	"bufio"
	"bytes"
	"fmt"
	"log"
	"strings"
)

func dropCR(data []byte) []byte {
	if len(data) > 0 && data[len(data)- 1] = =':' {
		return data[0 : len(data)- 1]}return data
}

func split(data []byte, atEOF bool) (advance int, token []byte, err error) {
	if atEOF && len(data) == 0 {
		return 0.nil.nil
	}
	if i := bytes.IndexByte(data, ':'); i >= 0 {
		// We have a full newline-terminated line.
		return i + 1, dropCR(data[0:i]), nil
	}
	// If we're at EOF, we have a final, non-terminated line. Return it.
	if atEOF {
		return len(data), dropCR(data), nil
	}
	// Request more data.
	return 0.nil.nil
}

func main(a) {
	/* Scanner method NewScanner creates Scanner Scanner.Split setting handler function Scanner.Scan gets the current token, Bytes Returns the token in []byte format.Text Returns the token in string format.Err Obtains the error */ returned by the processing method

	reader := strings.NewReader("aaa:bbb:ccc:ddd:eee:fff:ggg")
	scanner := bufio.NewScanner(reader)

	/* ScanBytes process tokens into single bytes ScanRunes Process tokens into UTF-8 encoded Unicode codes ScanWords separate tokens with Spaces tokenScanLines Separate tokens with newlines */
	// Replace the original default in the form of '\n' cut, the above two ends of the code is to imitate the source split function
	scanner.Split(split)
	count := 0
	for scanner.Scan() {
		log.Println(scanner.Text())
		count++
	}
	fmt.Println("In common", count, "Line")}Copy the code

10. Pipeline mode

  • Pipeline mode is a bit like streaming, similar to Linux echo 'hello world' |grep 'world'; This is also a common technique in GO development
package main

import (
    "fmt"
    "sync"
    "time"
)

type Cmd func(list []int) chan int
type PipeCmd func(in chan int) chan int// Support pipe functions

// Simulate getting data from the database
func GetData(list []int) chan int {
    c := make(chan int)
    go func(a) {
            defer close(c)
            for _, num := range list {
                    // Simulate the delay in getting data from the database
                    time.Sleep(time.Millisecond*500)
                    c <- num
            }
    }()

    return c

}


// Data processing
func Multiply10(in chan int) chan int { // This function supports pipes
    out := make(chan int)
    go func(a) {
            defer close(out)
            for num := range in {
                    // 1 second delay
                    time.Sleep(time.Second * 1)
                    out <- num * 10
            }
    }()
    return out
}

// pipe function
func Pipe(args []int, c1 Cmd, cs ... PipeCmd) chan int {
    ret := c1(args)
    if len(cs) == 0 {
            return ret
    }
    retList := make([]chan int.0)
    for index, c := range cs {
            if index == 0 {
                    retList = append(retList, c(ret))
            } else {
                    // Get the last one is the latest result
                    getChan := retList[len(retList)- 1]
                    retList = append(retList, c(getChan))
            }
    }
    return retList[len(retList)- 1]}func Test(testData []int) {
    // Use GetData to stuff data into a pipe, and then pass it back to the pipeline;
    ret := Pipe(testData, GetData, Multiply10, Multiply10)
    for r := range ret {
        fmt.Printf("%d ", r)
    }
}

func main(a) {
	Test([]int{1.2.3.4.5.6.7.8.9.10.11.12.13.14.15})}Copy the code

11. Pipeline pattern multiplexing

  • Pipeline multiplexing means that the data in the same pipeline are concurrently processed by different function entities, thus effectively improving the processing efficiency.

    Ps: Note that the pipeline needs to run concurrently.

package main

import (
	"fmt"
	"sync"
	"time"
)

type Cmd func(list []int) chan int
type PipeCmd func(in chan int) chan int// Support pipe functions

// Simulate getting data from the database
func GetData(list []int) chan int {
    c := make(chan int)
    go func(a) {
            defer close(c)
            for _, num := range list {
                    // Simulate the delay in getting data from the database
                    time.Sleep(time.Millisecond*500)
                    c <- num
            }
    }()

    return c
}


// Data processing
func Multiply10(in chan int) chan int { // This function supports pipes
    out := make(chan int)
    go func(a) {
            defer close(out)
            for num := range in {
                    // 1 second delay
                    time.Sleep(time.Second * 1)
                    out <- num * 10
            }
    }()
    return out
}


// Pipe multiplexing
func PipeM(args []int, c1 Cmd, cs ... PipeCmd) chan int {
    ret := c1(args) / / to find even
    out := make(chan int)
    wg := sync.WaitGroup{}
    for _, c := range cs {
        getChan := c(ret)
        wg.Add(1)
        go func(input chan int) {
                defer wg.Done()
                for v := range input {
                        // Simulate delay
                        time.Sleep(1*time.Second)
                        out <- v
                }
        }(getChan)
    }
    go func(a) {
        // Close the channel
        // If the pipe is not closed, the main thread reads will be deadlocked when all input coroutines are completed;
        defer close(out)
        wg.Wait()
    }()

    return out
}

func Test(testData []int) {
    // The data is returned directly to the pipeline
    ret := PipeM(testData, GetData, Multiply10, Multiply10)
    for r := range ret {
        fmt.Printf("%d ", r)
    }
}

func main(a) {
    Test([]int{1.2.3.4.5.6.7.8.9.10.11.12.13.14.15})}Copy the code
  • A encapsulated generic pipe pattern multiplexing
package pipe

import "sync"

type InChan chan interface{}
type OutChan chan interface{}
type CmdFunc func(args ...interface{}) InChan
type PipeCmdFunc func(in InChan) OutChan
type Pipe struct{
	Cmd CmdFunc
	PipeCmd PipeCmdFunc
	Count int
}

func NewPipe(a)  *Pipe {
	return &Pipe{Count:1}}// Set the function to get the data
func(this *Pipe) SetCmd(c CmdFunc)  {
	this.Cmd=c
}

// Set the pipe concurrency command
func(this *Pipe) SetPipeCmd(c PipeCmdFunc,count int )  {
	this.PipeCmd=c
	this.Count=count
}

func(this *Pipe) Exec(args ...interface{}) OutChan  {
	in:=this.Cmd(args)
	out:=make(OutChan)
	wg:=sync.WaitGroup{}
	for i:=0; i<this.Count; i++{ getChan:=this.PipeCmd(in) wg.Add(1)
		go func(input OutChan) {
			defer wg.Done()
			for v:=range input{
				out<-v
			}
		}(getChan)
	}
	go func(a) {
		defer close(out)
		wg.Wait()
	}()
	return out
}
Copy the code

12. Chain calls

package main

import (
	"fmt"
)

type UserAttrFunc func(*User)/ / setUserProperty of the function type
type UserAttrFuncs []UserAttrFunc
func(this UserAttrFuncs) apply(u *User)  {
	for _,f:=range this{
		f(u)
	}
}
func WithUserID(id int) UserAttrFunc  {
	return func(u *User) {
		u.Id=id
	}
}
func WithUserName(name string) UserAttrFunc  {
	return func(u *User) {
		u.Name=name
	}
}

type User struct {
	Id int
	Name string
}
func NewUser(fs ... UserAttrFunc) *User  {
	u:= new(User)
        // Cast to UserAttrFuncs
	UserAttrFuncs(fs).apply(u)
	return u
}

// make a chain call by returning itself
func (u *User) Mutate(fs ... UserAttrFunc) *User {
	UserAttrFuncs(fs).apply(u)
	return u
}

func main(a)  {
	u:=NewUser(
		WithUserName("lisi"),
		WithUserID(105),
	)
	fmt.Println(u)
        // chain call
	u.Mutate(WithUserID(1)).Mutate(WithUserName("zhangsan"))
	fmt.Println(u)

}

Copy the code

13. ErrReuslt Multi-value return encapsulation

  • Better unified error handling, which can be extended with this prototype;
package main

import (
	"encoding/json"
	"fmt"
	"sync"
)

type ErrorResult struct {
	data interface{}
	err  error
}

func (this *ErrorResult) Unwrap(a) interface{} {
	if(this.err ! =nil) {
		panic(this.err.Error())
	}
	return this.data
}

// Return data, err in certain format
func Result(values ...interface{}) *ErrorResult {
	if len(values) == 1 {
		if values[0] = =nil {
			return &ErrorResult{nil.nil}}if err, ok := values[0].(error); ok {
			return &ErrorResult{nil, err}
		}
	}

	if len(values) == 2 {
		if values[1] = =nil {
			return &ErrorResult{values[0].nil}}if err, ok := values[1].(error); ok {
			return &ErrorResult{values[0], err}
		}
	}
	err := fmt.Errorf("Result format error, valuses must be (err) or (data, err)")
	return &ErrorResult{nil, err}

}

func getErrorResult(a) (int, error)  {
	return 100, fmt.Errorf("It is a error.")}func getResult(a) (int, error)  {
	return 100.nil
}

func coreRun(a) (err error) {
	defer func(a) {
		if e := recover(a); e ! =nil {
			err = fmt.Errorf("%v", e)
		}
	}()
        // There is a panic Err interrupt
	Result(getErrorResult()).Unwrap()
        fmt.Println("exit")
	return
}

func main(a) {

	err := coreRun()
	fmt.Println(err)
}

Copy the code

14. JsonResult partial shipments

  • JsonResult Our web return value or command line return value often requires a uniform return; Look at the use of go decorators;
  • The following code is used in combination with code in section 13, using sync.pool;
package main

import (
	"encoding/json"
	"fmt"
	"sync"
)

type ErrorResult struct {
	data interface{}
	err  error
}

func (this *ErrorResult) Unwrap(a) interface{} {
	if(this.err ! =nil) {
		panic(this.err.Error())
	}
	return this.data
}

// Return data, err in certain format
func Result(values ...interface{}) *ErrorResult {
	if len(values) == 1 {
		if values[0] = =nil {
			return &ErrorResult{nil.nil}}if err, ok := values[0].(error); ok {
			return &ErrorResult{nil, err}
		}
	}

	if len(values) == 2 {
		if values[1] = =nil {
			return &ErrorResult{values[0].nil}}if err, ok := values[1].(error); ok {
			return &ErrorResult{values[0], err}
		}
	}
	err := fmt.Errorf("Result format error, valuses must be (err) or (data, err)")
	return &ErrorResult{nil, err}

}

type JsonResult struct {
	Message string      `json:"message"`
	Code    string      `json:"code"`
	Result  interface{} `json:"result"`
}

func NewJsonResult(message string, code string, result interface{}) *JsonResult {
	return &JsonResult{Message: message, Code: code, Result: result}
}

// sync.Pool is used to reuse used objects, saving the time of frequent resource creation and reclamation
var ResultPool *sync.Pool

func init(a) {
	ResultPool = &sync.Pool{
		New: func(a) interface{} {
			return NewJsonResult("".""."")}}}// JP function is just to show better decoupling of some state output
type JP func(code int, v interface{})
func jsonDumps(code int, v interface{}) {
	jb, _ := json.Marshal(v)
	fmt.Println(code, string(jb))
}

type Output func(j JP, v interface{})
type ResultFunc func(message string, code string, result interface{}) func(output Output)

func R(j JP) ResultFunc {
	return func(message string, code string, result interface{}) func(output Output) {
		r := ResultPool.Get().(*JsonResult)
		// Remember to put it back
		defer ResultPool.Put(r)

		r.Message = message
		r.Code = code
		r.Result = result
		return func(output Output) {
			output(j, r)
		}
	}
}

// If successful, return 200
func StateOk(j JP, v interface{}) {
	j(200, v)
}

// If it fails, return 500
func StateError(j JP, v interface{}) {
	j(500, v)
}

func getErrorResult(a) (int, error)  {
	return 100, fmt.Errorf("It is a error.")}func getResult(a) (int, error)  {
	return 100.nil
}


func main(a) {
	R(jsonDumps)("hello world"."1001", Result(getResult()).Unwrap())(StateOk)
	R(jsonDumps)("hello world"."1001", Result(getResult()).Unwrap())(StateError)
}


Copy the code