【问题标题】:Purpose of an explicitly scoped block in Go?Go 中显式范围块的目的?
【发布时间】:2019-10-10 18:14:37
【问题描述】:

我正在阅读 MicroMDM SCEP 存储库中的此源代码,https://github.com/micromdm/scep/blob/1e0c4b782f3f2e1e6f81da5f82444a6cedc89df3/cmd/scepclient/scepclient.go#L54-L65

func run(cfg runCfg) error {
    ctx := context.Background()
    var logger log.Logger
    {
        if strings.ToLower(cfg.logfmt) == "json" {
            logger = log.NewJSONLogger(os.Stderr)
        } else {
            logger = log.NewLogfmtLogger(os.Stderr)
        }
        stdlog.SetOutput(log.NewStdlibAdapter(logger))
        logger = log.With(logger, "ts", log.DefaultTimestampUTC)
        if !cfg.debug {
            logger = level.NewFilter(logger, level.AllowInfo())
        }
    }
    lginfo := level.Info(logger)

我想知道显式块(外部{ ... })的目的是什么?这段代码会不会和它们被删除时不完全一样,比如

func run(cfg runCfg) error {
    ctx := context.Background()
    var logger log.Logger
    if strings.ToLower(cfg.logfmt) == "json" {
        logger = log.NewJSONLogger(os.Stderr)
    } else {
        logger = log.NewLogfmtLogger(os.Stderr)
    }
    stdlog.SetOutput(log.NewStdlibAdapter(logger))
    logger = log.With(logger, "ts", log.DefaultTimestampUTC)
    if !cfg.debug {
        logger = level.NewFilter(logger, level.AllowInfo())
    }
    lginfo := level.Info(logger)

也许显式块只是为了提高可读性?

【问题讨论】:

  • 没有目的。

标签: go lexical-scope


【解决方案1】:

在这种情况下,额外的块似乎没有任何意义。块内没有声明任何变量。它不会增加清晰度,反而会让您感到困惑。

如果需要清晰,您可以将该代码提取到一个新函数中以初始化记录器。

func initLogger(cfg runCfg) log.Logger {
    var logger log.Logger

    if strings.ToLower(cfg.logfmt) == "json" {
        logger = log.NewJSONLogger(os.Stderr)
    } else {
        logger = log.NewLogfmtLogger(os.Stderr)
    }
    stdlog.SetOutput(log.NewStdlibAdapter(logger))
    logger = log.With(logger, "ts", log.DefaultTimestampUTC)
    if !cfg.debug {
        logger = level.NewFilter(logger, level.AllowInfo())
    }

    return logger
}

func run(cfg runCfg) error {
    ctx := context.Background()
    logger := initLogger(cfg)
    lginfo := level.Info(logger)
    ...

我最好的猜测是这个块在过去曾有过某种用途,而更改代码的人并没有将其删除,也可能不确定是否仍然有用途。翻看这个函数的责备日志或许会给你一个答案。

【讨论】:

  • 您使用错误的参数调用initLogger。它需要一个配置,而不是上下文类型。
  • @colminator 谢谢,已修复。
【解决方案2】:

花点时间阅读其余代码,这些块是all over the code。但是,在这些情况下,声明了 短期变量,这是一种在不再需要它们后将其丢弃的方法。两种最有可能的情况是记录器可能有一些,或者它只是开发人员选择的特定样式。实际上,两者都可能是真的。

这对 Go 来说有点奇怪,但它可以有效地明确告诉垃圾收集器以及开发人员这些已经超出范围。然而,Go 编译器和 GC 在撰写本文时已经足够先进,可以知道何时可以丢弃它们,因此除了整理当前作用域的命名空间之外,程序本身几乎没有什么好处。

虽然我同意@Schwern,但它增加了清晰度并实现了相同的结果,将这些重构为自己的功能。如果有人花时间明确声明作用域块,那么为什么不利用这段时间来使它们发挥作用呢?

链接代码块:

var svc scepserver.Service // scep service
{
    svcOptions := []scepserver.ServiceOption{
        scepserver.ChallengePassword(*flChallengePassword),
        scepserver.WithCSRVerifier(csrVerifier),
        scepserver.CAKeyPassword([]byte(*flCAPass)),
        scepserver.ClientValidity(clientValidity),
        scepserver.AllowRenewal(allowRenewal),
        scepserver.WithLogger(logger),
    }
    svc, err = scepserver.NewService(depot, svcOptions...)
    if err != nil {
        lginfo.Log("err", err)
        os.Exit(1)
    }
    svc = scepserver.NewLoggingService(log.With(lginfo, "component", "scep_service"), svc)
}

var h http.Handler // http handler
{
    e := scepserver.MakeServerEndpoints(svc)
    e.GetEndpoint = scepserver.EndpointLoggingMiddleware(lginfo)(e.GetEndpoint)
    e.PostEndpoint = scepserver.EndpointLoggingMiddleware(lginfo)(e.PostEndpoint)
    h = scepserver.MakeHTTPHandler(e, svc, log.With(lginfo, "component", "http"))
}

【讨论】:

  • 如果一个代码块只使用一次(或只从一个位置调用),将其移动到(全局)函数会使逻辑远离它真正所属的位置。这肯定是一种风格,但从可读性的角度来看,嵌套代码确实一眼就能看出它在代码层次结构中的位置。
  • 即使需要一个函数(即需要从 2 个或多个位置调用)但仅在单个函数/方法范围内,定义内联函数也传达了有意义的设计。
  • @colminator 注意,这个函数是包私有的。函数不仅仅是调用两次相同的代码。逻辑没有移开,调用还在,只是封装了细节。读者现在可以选择在较高级别浏览:获取背景上下文,初始化记录器,将日志级别设置为 info。如果读者想了解有关特定部分的更多详细信息,他们可以选择性地阅读函数文档和代码。最后,为逻辑赋予其自己的功能允许对其进行命名、记录、单元测试、独立重构和重用
  • 没有错误的答案。它只是一种风格。但即使该函数是一个小写的非公共函数,理论上它也可以从任何地方调用——这就是我所说的“全局”,即全局范围意义。至于单元测试,应该在外层进行——尤其是在内部函数相对简单的情况下。
  • 我对使用函数的看法与@Schwern 所说的完全一样。我确实同意这部分是风格问题,但更小的、离散的代码块作为函数使事情更清晰,更容易进行单元测试。此处链接的代码可能只是两个函数,然后在读取调用函数时,更容易看到这两个操作,而不需要所有不必要的元/配置信息来混淆函数。
猜你喜欢
  • 2018-12-05
  • 1970-01-01
  • 2021-09-16
  • 1970-01-01
  • 1970-01-01
  • 2021-09-16
  • 2011-09-04
  • 1970-01-01
  • 2014-05-24
相关资源
最近更新 更多