複数の人に挨拶を返す

モジュールのコードに最後に追加する変更では、1つのリクエストで複数の人に対する挨拶を取得するサポートを追加します。言い換えれば、複数の値の入力を処理し、その入力の値を複数の値の出力とペアにします。これを行うには、一連の名前を、それぞれに挨拶を返すことができる関数に渡す必要があります。

しかし、問題があります。Hello 関数のパラメーターを単一の名前から一連の名前に変更すると、関数のシグネチャが変わってしまいます。もし example.com/greetings モジュールをすでに公開しており、ユーザーがすでに Hello を呼び出すコードを記述していた場合、その変更は彼らのプログラムを壊すことになります。

この状況では、別の名前の新しい関数を作成する方が良い選択です。新しい関数は複数のパラメーターを受け取ります。これにより、古い関数は後方互換性のために保持されます。

  1. greetings/greetings.go で、コードを次のように変更します。
    package greetings
    
    import (
        "errors"
        "fmt"
        "math/rand"
    )
    
    // Hello returns a greeting for the named person.
    func Hello(name string) (string, error) {
        // If no name was given, return an error with a message.
        if name == "" {
            return name, errors.New("empty name")
        }
        // Create a message using a random format.
        message := fmt.Sprintf(randomFormat(), name)
        return message, nil
    }
    
    // Hellos returns a map that associates each of the named people
    // with a greeting message.
    func Hellos(names []string) (map[string]string, error) {
        // A map to associate names with messages.
        messages := make(map[string]string)
        // Loop through the received slice of names, calling
        // the Hello function to get a message for each name.
        for _, name := range names {
            message, err := Hello(name)
            if err != nil {
                return nil, err
            }
            // In the map, associate the retrieved message with
            // the name.
            messages[name] = message
        }
        return messages, nil
    }
    
    // randomFormat returns one of a set of greeting messages. The returned
    // message is selected at random.
    func randomFormat() string {
        // A slice of message formats.
        formats := []string{
            "Hi, %v. Welcome!",
            "Great to see you, %v!",
            "Hail, %v! Well met!",
        }
    
        // Return one of the message formats selected at random.
        return formats[rand.Intn(len(formats))]
    }
    

    このコードでは、以下のことを行います。

    • 単一の名前ではなく、名前のスライスをパラメーターとする Hellos 関数を追加します。また、戻り値の型の1つを string から map に変更して、名前を挨拶メッセージにマッピングして返すことができます。
    • 新しい Hellos 関数が既存の Hello 関数を呼び出すようにします。これにより、両方の関数を残しつつ、重複を減らすことができます。
    • 受信した各名前をキーとして、生成されたメッセージを値として関連付ける messages マップを作成します。Go では、マップを次の構文で初期化します: make(map[キーの型]値の型)Hellos 関数がこのマップを呼び出し元に返すようにします。マップの詳細については、Go ブログのGo maps in actionを参照してください。
    • 関数が受け取った名前をループし、それぞれが空でない値を持っていることを確認してから、各名前に関連するメッセージを関連付けます。この for ループでは、range は2つの値を返します。ループ内の現在の項目のインデックスと、項目の値のコピーです。インデックスは必要ないので、Go のブランク識別子 (アンダースコア) を使用して無視します。詳細については、Effective Go のThe blank identifierを参照してください。
  2. hello/hello.go の呼び出しコードで、名前のスライスを渡し、返された名前/メッセージマップの内容を出力します。

    hello.go で、コードを次のように変更します。

    package main
    
    import (
        "fmt"
        "log"
    
        "example.com/greetings"
    )
    
    func main() {
        // Set properties of the predefined Logger, including
        // the log entry prefix and a flag to disable printing
        // the time, source file, and line number.
        log.SetPrefix("greetings: ")
        log.SetFlags(0)
    
        // A slice of names.
        names := []string{"Gladys", "Samantha", "Darrin"}
    
        // Request greeting messages for the names.
        messages, err := greetings.Hellos(names)
        if err != nil {
            log.Fatal(err)
        }
        // If no error was returned, print the returned map of
        // messages to the console.
        fmt.Println(messages)
    }
    

    これらの変更により、

    • 3つの名前を保持するスライス型の 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 の組み込み機能を使用してコードの単体テストを作成します。