目录

"有趣"的go代码案例

一些不好的案例

case1:Jalang

config.go

package config

var CONFIG *config

type config struct {
    Host string
    Port string
}

func GetHost() string {
    return CONFIG.Host
}

func GetPort() string {
    return CONFIG.Port
}

main.go

func main() {
    // ... init config
	engine := gin.Default()
	engine.RunListener(config.GetHost()+":"+config.GetPort())
}
理解
猜测原代码是想保护原配置不被其他地方更改,可以直接把config.go里面的var CONFIG *config变为私有,如果没有保护配置要求,实际上可以直接 config.CONFIG.Host+":"+config.CONFIG.Post

case2:消失的const

utils.go

func IsOver(size int) bool{
    if size > 10 {
        return true
    }
    return false
}

use.go

if (IsOver(2)) {
    return
}
理解
原代码中IsOver被多处调用,可能原编码人想着抽出来,但我感觉这里没必要单写一个函数,直接const defaultSize = 10,然后if size>defaultSize {return},这样可以增加可阅读性,第三个人读代码时就不必在进行一次跳转。

case3:不一定要判空

func Deal(items []string) {
    if items == nil {
        return
    }
    for _,item := items {
        ....
    }
}
理解
当切片为nil的时候,无论是对其进行for循环还是取长度都不会panic。

case4: 如果有如果

func If1(a *int) {
    if a == nil {
        return
    }
    if a > 10 {
        ...
    }
    ...
}
理解

两个if可以合并

func If1(a *int) {
    if a = nil && a > 10 {
        ...
    }
    ...
}

case5: 大概率是多余的

log.go

package logger

var Log *logger

type Logger struct {l log}

func InitLogger() {
    Log = &logger{l: log.NewLog()}
}

func (l *logger) Info(content string,args ...interface) {
    ....
}

func (l *logger) Error(content string,args ...interface) {
    ....
}

use.go

func main() {
    logger.Logger.Info("hello")
}
理解

可以简化,使用的时候做到像如下:

logger.Info("hello")

如何优雅地写go

隐式实现

golang的接口都是隐式实现的,如果某个A接口中的方法在B接口存在,那么实现了B接口的方法也会间接实现A接口,这会降低我们的代码可读性。

type Person interface {
	GetName() string
	GetAge() int
	GetHeight() int
}

type Animal interface {
	GetName() string
	GetAge() int
}

type Chinese struct {
}

func (c *Chinese) GetName() string {
	return "zhang"
}

func (c *Chinese) GetAge() int {
	return 16
}

func (c *Chinese) GetHeight() int {
	return 180
}

当我只想实现Person接口的时候,由于隐式实现的规则,Animal也被实现了,如下图: /posts/interesting_code/%E8%A2%AB%E5%A4%9A%E5%AE%9E%E7%8E%B0.png

可以加一些特殊的方法,如下代码中的两个接口,可以加一些特殊方法区分(此场景建立在后续会对这两个接口调整的情况,要是没有后续修改计划,实际上这两个接口可以合并)

type Person interface {
    Person()
    GetName() string
    GetAge() int
    GetHeight() int
}

type Animal interface {
    Animal()
    GetName() string
    GetAge() int
}

如果不想在接口中增加特殊方法,可以强制定义实现

type Person interface {
    Person()
    GetName() string
    GetAge() int
    GetHeight() int
}

type Animal interface {
    GetName() string
    GetAge() int
}

use.go


var _ Person = &Chinese{}

type Chinses struct {}

func (c *Chinses) GetName() string {
    return "zhang"
}

func (c *Chinses) GetAge() int {
    return 18
}

这样做的好处是可以找到所有真正实现了该接口的 /posts/interesting_code/%E9%9A%90%E5%BC%8F%E5%AE%9E%E7%8E%B0.png

巧妙利用接口

有些属性,我们可以在接口中定义,比如下面例子的name。
反例:

type Person interface {
	Age() int
	Height() int
	Wish() string
}

type Router struct {
	router map[string]Person
}

func NewRouter() *Router {
	return &Router{
		router: make(map[string]Person),
	}
}

func (r *Router) Bind(name string, person Person) {
	r.router[name] = person
}

func (r *Router) Run(name string) {
	f, ok := r.router[name]
	if !ok {
		fmt.Println("name not found")
		return
	}
	fmt.Printf("my name is %s\n,%d years old,my wish is %s", name, f.Age(), f.Wish())
}

use.go

func main() {
	router := NewRouter()
	router.Bind((&Chinese{}).Name(), &Chinese{})
	router.Bind((&Russian{}).Name(), &Russian{})
	router.Run("zhang")
}

优化:

type Person interface {
    Name() string
	Age() int
	Height() int
	Wish() string
}

type Router struct {
	router map[string]Person
}

func NewRouter() *Router {
	return &Router{
		router: make(map[string]Person),
	}	
}

func (r *Router) Add(person Person) {
	r.router[person.Name()] = person
}

func (r *Router) Run(name string) {
	f, ok := r.router[name]
	if !ok {
		fmt.Println("name not found")
		return
	}
	fmt.Printf("my name is %s\n,%d years old,my wish is %s", f.Name(), f.Age(), f.Wish())
}

use.go

func main() {
	router := NewRouter()
	router.Add(&Chinese{})
	router.Add(&Russian{})
	router.Run("zhang")
}