複数人への挨拶を返す

モジュールのコードへの最後の変更では、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関数を追加します。また、戻り値の型をstringからmapに変更して、名前を挨拶メッセージにマッピングして返すことができるようにします。
    • 新しいHellos関数に既存のHello関数を呼び出させます。これにより、両方の関数をそのまま残しながら、重複を減らすことができます。
    • 受信した各名前(キーとして)を生成されたメッセージ(値として)に関連付けるmessagesマップを作成します。Goでは、マップは次の構文で初期化します: make(map[キーの型]値の型)Hellos関数はこのマップを呼び出し元に返します。マップの詳細については、GoブログのGoのマップの動作を参照してください。
    • 関数が受信した名前をループ処理し、それぞれが空でない値を持っていることを確認し、その後、各名前をメッセージと関連付けます。このforループでは、rangeは2つの値を返します。ループ内の現在のアイテムのインデックスとアイテムの値のコピーです。インデックスは不要なため、Goの空白識別子(アンダースコア)を使用して無視します。詳細については、Effective Goの空白識別子を参照してください。
  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!]
    

このトピックでは、名前/値ペアを表すためのマップを紹介しました。また、モジュールで新機能や変更された機能を実装するために新しい関数を実装することで、下位互換性を維持するという考え方も紹介しました。下位互換性の詳細については、モジュールの互換性を維持するを参照してください。

次に、組み込みのGo機能を使用して、コードのユニットテストを作成します。