为多人返回问候语

在您对模块代码进行的最后修改中,您将添加支持在一次请求中为多人获取问候语的功能。换句话说,您需要处理多值输入,然后将输入中的值与多值输出进行配对。为此,您需要将一组名字传递给一个能够为每个名字返回问候语的函数。

但这里有个问题。将Hello函数的参数从单个名字改为一组名字会改变函数的签名。如果您已经发布了example.com/greetings模块,并且用户已经编写了调用Hello的代码,那么这个更改会破坏他们的程序。

在这种情况下,更好的选择是编写一个具有不同名称的新函数。新函数将接受多个参数。这样可以保留旧函数以实现向后兼容。

  1. 在 greetings/greetings.go 中,将代码修改为如下所示。
    package greetings
    
    import (
        "errors"
        "fmt"
        "math/rand"
    )
    
    // Hello 为指定的人返回问候语。
    func Hello(name string) (string, error) {
        // 如果未提供名字,则返回带有消息的错误。
        if name == "" {
            return name, errors.New("empty name")
        }
        // 使用随机格式创建消息。
        message := fmt.Sprintf(randomFormat(), name)
        return message, nil
    }
    
    // Hellos 返回一个映射,该映射将每个指定的人与问候消息关联起来。
    func Hellos(names []string) (map[string]string, error) {
        // 一个用于将名字与消息关联的映射。
        messages := make(map[string]string)
        // 遍历接收到的名字切片,为每个名字调用 Hello 函数以获取消息。
        for _, name := range names {
            message, err := Hello(name)
            if err != nil {
                return nil, err
            }
            // 在映射中,将检索到的消息与名字关联。
            messages[name] = message
        }
        return messages, nil
    }
    
    // randomFormat 从一组问候消息中返回一个。返回的消息是随机选择的。
    func randomFormat() string {
        // 消息格式的切片。
        formats := []string{
            "Hi, %v. Welcome!",
            "Great to see you, %v!",
            "Hail, %v! Well met!",
        }
    
        // 随机选择并返回一个消息格式。
        return formats[rand.Intn(len(formats))]
    }
    

    在此代码中,您:

    • 添加了一个Hellos函数,其参数是名字的切片而非单个名字。同时,您将其返回类型之一从string更改为map,以便可以返回名字到问候消息的映射。
    • 让新的Hellos函数调用现有的Hello函数。这有助于减少重复,同时保留两个函数。
    • 创建一个messages映射,用于将接收到的每个名字(作为键)与生成的消息(作为值)关联起来。在Go中,您使用以下语法初始化映射:make(map[键类型]值类型)。您让Hellos函数将此映射返回给调用者。有关映射的更多信息,请参阅Go博客上的Go maps in action
    • 遍历函数接收到的名字,检查每个名字是否具有非空值,然后为每个名字关联一条消息。在此for循环中,range返回两个值:循环中当前项的索引和该项值的副本。您不需要索引,因此使用Go的空标识符(下划线)忽略它。更多信息请参阅Effective Go中的The blank identifier
  2. 在您的 hello/hello.go 调用代码中,传递一个名字切片,然后打印返回的名字/消息映射的内容。

    在 hello.go 中,将代码修改为如下所示。

    package main
    
    import (
        "fmt"
        "log"
    
        "example.com/greetings"
    )
    
    func main() {
        // 设置预定义Logger的属性,包括日志条目前缀和禁用打印时间、源文件及行号的标志。
        log.SetPrefix("greetings: ")
        log.SetFlags(0)
    
        // 名字的切片。
        names := []string{"Gladys", "Samantha", "Darrin"}
    
        // 请求这些名字的问候消息。
        messages, err := greetings.Hellos(names)
        if err != nil {
            log.Fatal(err)
        }
        // 如果未返回错误,则将返回的消息映射打印到控制台。
        fmt.Println(messages)
    }
    

    通过这些更改,您:

    • 创建了一个names变量,作为包含三个名字的切片类型。
    • names变量作为参数传递给Hellos函数。
  3. 在命令行中,切换到包含 hello/hello.go 的目录,然后使用go run确认代码是否正常工作。

    输出应该是一个将名字与消息关联起来的映射的字符串表示,类似于以下内容:

    $ go run .
    map[Darrin:Hail, Darrin! Well met! Gladys:Hi, Gladys. Welcome! Samantha:Hail, Samantha! Well met!]
    
    本主题介绍了使用映射来表示名称/值对的概念。同时也阐述了通过在新功能或功能变更时,为模块实现新函数以保持向后兼容性的思路。关于向后兼容性的更多信息,请参阅保持模块兼容性。 接下来,您将使用Go语言内置功能为代码创建单元测试。