Возврат приветствий для нескольких людей

В последних изменениях, которые вы внесёте в код вашего модуля, вы добавите поддержку получения приветствий для нескольких людей в одном запросе. Другими словами, вы будете обрабатывать многозначный входной параметр, а затем сопоставлять значения этого параметра с многозначным выходным результатом. Для этого вам нужно будет передать набор имён в функцию, которая может вернуть приветствие для каждого из них.

Но есть одна особенность. Изменение параметра функции 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
    }
    <ins>// 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
    }</ins>
    // 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[key-type]value-type). Функция Hellos возвращает эту карту вызывающей стороне. Более подробно о картах можно прочитать в статье Go maps in action в блоге Go.
    • Проходит по всем полученным именам, проверяет, что каждое из них не пустое, а затем сопоставляет с сообщением. В этом цикле for range возвращает два значения: индекс текущего элемента в цикле и копию значения элемента. Вам не нужен индекс, поэтому вы используете пустой идентификатор Go (подчёркивание) для его игнорирования. Более подробно см. The blank identifier в Effective Go.
  2. В коде вызова в hello/hello.go передайте срез имён, а затем распечатайте содержимое карты имён/сообщений, которую вы получите.

    В hello.go измените код так, чтобы он выглядел следующим образом.

    package main
    import (
      "fmt"
      "log"
      "example.com/greetings"
    )
    func main() {
      // Устанавливает свойства предопределённого Logger, включая
      // префикс записи журнала и флаг, чтобы отключить вывод
      // времени, файла и номера строки.
      log.SetPrefix("greetings: ")
      log.SetFlags(0)
      <ins>// Срез имён.
      names := []string{"Gladys", "Samantha", "Darrin"}
      // Запрашивает сообщения приветствия для имён.
      messages, err := greetings.Hellos(names)</ins>
      if err != nil {
        log.Fatal(err)
      }
      <ins>// Если ошибок не было, распечатывает возвращённую карту
      // сообщений в консоль.
      fmt.Println(messages)</ins>
    }
    

    С этими изменениями вы:

    • Создаёте переменную 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!]
    

В этой теме были представлены карты для представления пар имя/значение. Также была рассмотрена идея сохранения обратной совместимости путём реализации новой функции для новой или изменённой функциональности в модуле. Более подробно о совместимости модулей можно прочитать в статье Keeping your modules compatible.

Далее вы будете использовать встроенные функции Go для создания модульного теста для своего кода.

GoRu.dev Golang на русском

На сайте представлена адаптированная под русский язык документация языка программирования Golang