• 6.2.1. 不鼓励使用 nil 作为参数

    6.2.1. 不鼓励使用 nil 作为参数

    本章开始时我建议是不要强迫提供给 API 的调用者他们不在乎的参数。 这就是我要说的为默认用例设计 API。

    这是 net/http 包中的一个例子

    1. package http
    2. // ListenAndServe listens on the TCP network address addr and then calls
    3. // Serve with handler to handle requests on incoming connections.
    4. // Accepted connections are configured to enable TCP keep-alives.
    5. //
    6. // The handler is typically nil, in which case the DefaultServeMux is used.
    7. //
    8. // ListenAndServe always returns a non-nil error.
    9. func ListenAndServe(addr string, handler Handler) error {

    ListenAndServe 有两个参数,一个用于监听传入连接的 TCP 地址,另一个用于处理 HTTP 请求的 http.HandlerServe 允许第二个参数为 nil,需要注意的是调用者通常会传递 nil,表示他们想要使用 http.DefaultServeMux 作为隐含参数。

    现在,Serve 的调用者有两种方式可以做同样的事情。

    1. http.ListenAndServe("0.0.0.0:8080", nil)
    2. http.ListenAndServe("0.0.0.0:8080", http.DefaultServeMux)

    两者完全相同。

    这种 nil 行为是病毒式的。 http 包也有一个 http.Serve 帮助类,你可以合理地想象一下 ListenAndServe 是这样构建的

    1. func ListenAndServe(addr string, handler Handler) error {
    2. l, err := net.Listen("tcp", addr)
    3. if err != nil {
    4. return err
    5. }
    6. defer l.Close()
    7. return Serve(l, handler)
    8. }

    因为 ListenAndServe 允许调用者为第二个参数传递 nil,所以 http.Serve 也支持这种行为。 事实上,http.Serve 实现了如果 handlernil,使用 DefaultServeMux 的逻辑。 参数可为 nil 可能会导致调用者认为他们可以为两个参数都使用 nil。 像下面这样:

    1. http.Serve(nil, nil)

    会导致 panic

    贴士:不要在同一个函数签名中混合使用可为 nil 和不能为 nil 的参数。

    http.ListenAndServe 的作者试图在常见情况下让使用 API 的用户更轻松些,但很可能会让该程序包更难以被安全地使用。

    使用 DefaultServeMux 或使用 nil 没有什么区别。

    1. const root = http.Dir("/htdocs")
    2. http.Handle("/", http.FileServer(root))
    3. http.ListenAndServe("0.0.0.0:8080", nil)

    对比

    1. const root = http.Dir("/htdocs")
    2. http.Handle("/", http.FileServer(root))
    3. http.ListenAndServe("0.0.0.0:8080", http.DefaultServeMux)

    这种混乱值得拯救吗?

    1. const root = http.Dir("/htdocs")
    2. mux := http.NewServeMux()
    3. http.Handle("/", http.FileServer(root))
    4. http.ListenAndServe("0.0.0.0:8080", mux)

    贴士: 认真考虑 helper 函数会节省不少时间。 清晰要比简洁好。

    贴士:避免公共 API 使用测试参数避免在公开的 API 上使用仅在测试范围上不同的值。 相反,使用 Public wrappers 隐藏这些参数,使用辅助方式来设置测试范围中的属性。