Generator Pattern

Generator PatternはGo言語における並列処理パターンの一つで、goroutine-safeな値列の生成などに使用することができます。 コードを見た方が早いと思いますので、コードを掲載しましょう。 次の例は複数のgoroutineから共通の連番を採番したいときに利用することができます。 1 2 3 4 5 6 7 8 9 10 11 12 13 func GenInt(ctx context.Context, max int) <-chan int { ch := make(chan int) go func() { defer close(ch) for i := 0; i < max; i++ { select { case <-ctx.Done(): return case ch <- i: } } return ch } 返された<-chan intからintの値を取得するようにすることで、重複のない連番を取得することができます。 Go言語において、chanは複数箇所から値の取り出しを行うことができますが、chanに入力された一つの値はどこか一箇所からしか取り出すことができません。そのため、lock等を使用しなくとも、必ず重複無く連番を取得することができます。 lockを使用した場合、若干動作が遅いため、可能であればlockを使用しないで、chanを使用して実装できるとより高速な、Goらしいコードとすることができます。

2018-11-21 · nasa9084

Future Pattern

Future Patternは非同期処理パターンの一つで、ある処理を別のスレッドなどで実行し、結果を後で(=未来で)受け取るような処理に用いられるデザインパターンです。 特徴としては、外側に見えている関数などの処理を実行するオブジェクトは、処理を別スレッドに委譲し、後で結果を得ることの出来るFutureと呼ばれるオブジェクトを即座にメインロジックへと返却することです。 言葉で書いても、何だかよくわからないので、コードを見てみましょう。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 /* package, import part */ func main() { in := make(chan int) out := Double(in) // この時点では結果は得られない go func() { for i := 0; i < 10; i++ { in <- i } close(in) }() for d := range out { fmt.Println(d) // ここで結果を得る } } func Double(in <-chan int) <-chan int { out := make(chan int) go func() { for i := range in { out <- 2 * i } close(out) } return out // Futureオブジェクト } main関数から呼び出されたDouble関数は、与えられた数を二倍する関数ですが、二倍する処理は呼び出された時点では実行せず、即座にchannelを返します。この、変数名outのchannelがFutureオブジェクトです。 そのため、数を二倍した結果は、Double関数を呼び出した時点では得られず、後でoutchannelから得ることとなります。 ...

2018-07-04 · nasa9084

Functional Option Pattern

Fuctional Option PatternはGo言語において構造体の初期化時にオプション引数を与えるためのデザインパターンで、元ネタはRob Pike氏のSelf-referential functions and the design of options 、Dave Cheney氏のFunctional options for frendly APIS です。 Go言語には他の言語でオプション引数やキーワード引数と呼ばれる、省略可能な引数が存在しません。 通常は大きな問題は無いのですが、しかし、構造体の初期化時には、省略可能引数がほしくなる場合もあります。 Dave Cheney氏の記事にもある例を見てみましょう。 例 1 2 3 4 5 6 7 8 9 10 11 12 13 type Server struct { listener net.Listener } func NewServer(addr string) (*Server, error) { l, err := net.Listen("tcp", addr) if err != nil { return nil, err } srv := Server{listener: l} go srv.run return &srv, nil } よくある構造体の初期化関数です。 初期化が上手くいけば、ポインタとnilを、上手くいかなければnilとエラーを返す形になっています。 ここで、Serverになにがしかの拡張を加えることを考えます。たとえばタイムアウトや、TLS対応等です。 しかしこれらは指定する必要が無い場合もあります。 Go言語を用いたアプローチですぐに思いつくのは、オプションの組み合わせの数だけ初期化関数を作成することですね。(たとえば、With...というサフィックスを使って) しかしこれは、オプションの数が増えると、作成しなければならない関数の数が膨大になっていきます。 保守の観点から見てもこれは余りうれしくありません。 Config構造体を用いる そこでよく用いられるのが、設定を保持する構造体を用いる方法です。 例としては、以下の様にします。 1 2 3 4 5 6 7 8 type Config struct { Timeout time.Duration Cert *tls.Cert } func NewServer(addr string, config Config) { // ... } これも良く用いられているパターンです。 しかし、オプションを一切与えない場合のパターンを考えてみましょう。 ...

2017-09-26 · nasa9084