关于go:编写好的Golang代码

Writing good Golang code

我正在掌握戈兰做事的方法。我将非常感谢任何能够在以下方面提供帮助的人。首先是一些示例代码

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
package main

import (
   "log"
   "os"
)

func logIt(s string) {
    f, _ := os.OpenFile("errors.log", os.O_RDWR|os.O_CREATE|os.O_APPEND,
        0666)
    defer f.Close()

    log.SetOutput(f)
    log.Println(s)
}

type iAm func(string)

func a(iam string) { logIt(iam +" A") }

func b(iam string) { logIt(iam +" B") }

func c(iam string) { logIt(iam +" C") }

var funcs = map[string]iAm{"A": a,"B": b,"C": c}

func main() {
    funcs["A"]("Je suis")
    funcs["B"]("Ich bin")
    funcs["A"]("Yo soy")
    funcs["D"]("Soy Yo")
}

解释

  • 我将所有的日志输出传输到一个文件中,以便稍后监控它。这是去频道的路吗?
  • 我想根据用户输入确定在运行时调用的正确函数。为此,我将函数打包为一个golang映射-在PHP中,我将使用一个关联数组。这是可行的。然而,这是一种有效的做事方式吗?
  • 最后,你会注意到我的地图上实际上没有D键。最后一个函数调用导致抛出一个摆动。在另一种语言中,我会用一次尝试来包装这些调用…阻止并避免了问题。据我所知,Go的理念是先检查钥匙的有效性,然后恐慌,而不是盲目使用钥匙。对吗?

我是一个新手,所以我可能有其他语言的行李。在我看来,以先发制人的方式处理特殊情况(使用前先检查钥匙)既不明智,也不高效。对吗?


登录到文件

每次我想记录一些东西时,都不会打开和关闭文件。在启动时,我只打开它一次并将其设置为输出,在程序存在之前,关闭它。我不会使用logIt()函数:只需使用log包的函数进行日志记录,就可以进行格式化日志记录,例如使用log.Printf()等。

动态功能选择

函数映射完全正常,并且性能良好。如果需要更快的方法,可以根据函数名执行switch,并直接调用case分支中的目标函数。

检查钥匙是否存在

map中的值是函数值。函数类型的零值是nil,不能调用nil函数,因此在继续调用之前必须检查该值。注意,如果用一个不存在的键索引一个映射,则返回值类型的零值,对于函数类型,该值类型为nil。因此,我们可以简单地检查值是否为nil。还有另一个逗号ok习语,例如fv, ok := funcs[name],其中ok是一个布尔值,用于指示是否在映射中找到了键。

但是,您可以在一个地方进行,您不必在每个呼叫中重复它:

1
2
3
4
5
func call(name, iam string) {
    if fv := funcs[name]; fv != nil {
        fv(iam)
    }
}

注:

如果您选择使用switchdefault分支将处理无效的函数名(这里您当然不需要函数映射):

1
2
3
4
5
6
7
8
9
10
11
12
13
func call(name, iam string) error {
    switch name {
    case"A":
        a(iam)
    case"B":
        b(iam)
    case"C":
        c(iam)
    default:
        return errors.New("Unknown function:" + name)
    }
    return nil
}

号错误处理/报告

In-Go函数可以有多个返回值,因此In-Go通过返回error值来传播错误,即使函数通常具有其他返回值。

因此,如果找不到指定的函数,call()函数应该有一个error返回类型为signal。

您可以选择返回由errors.New()函数(因此它可以是动态的)等创建的新error值,也可以选择创建全局变量并具有固定的错误值,例如:

1
var ErrInvalidFunc = errors.New("Invalid function!")

此解决方案的优点是,call()函数的调用者可以将返回的error值与ErrInvalidFunc全局变量的值进行比较,以了解情况并采取相应的行动,例如:

1
2
3
4
5
6
7
if err := call("foo","bar"); err != nil {
    if err == ErrInvalidFunc {
        //"foo" is an invalid function name
    } else {
        // Some other 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
26
27
28
29
30
31
32
33
34
package main

import ("errors";"log";"os")

type iAm func(string)

func a(iam string) { log.Println(iam +" A") }
func b(iam string) { log.Println(iam +" B") }
func c(iam string) { log.Println(iam +" C") }

var funcs = map[string]iAm{"A": a,"B": b,"C": c}

func main() {
    f, err := os.OpenFile("errors.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
    if err != nil {
        panic(err)
    }
    defer f.Close()
    log.SetOutput(f)

    call("A","Je suis")
    call("B","Ich bin")
    call("C","Yo soy")
    call("D","Soy Yo")
}

func call(name, iam string) error {
    if fv := funcs[name]; fv == nil {
        return errors.New("Unknown funcion:" + name)
    } else {
        fv(iam)
        return nil
    }
}