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 clarity
reflect.Kind
和reflect.Value
The 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