2014年1月13日月曜日

Golang Cafe #12を開催しました。

Golang Cafe #12を開催しました。
用意したサンプルコードは、githubにおいてありますので、ごらんください。

今回は、いつも3人のところ、四国からも参加があり、4人での回になりました。
(毎回新しく参加される方がいなかったので、3人揃った時点で監視をやめてしまいました。次回からアラームをつけておこうと思います)

今回は、compressパッケージを見ていくということで、bzip2/flate/gzipの3つのサンプルを動かしました。他にも、lzwとzlibパッケージもありますが、ほぼ同じコードになってしまうことと、実際の用途が思いつかなかったので、現時点でよく利用されている3つのパッケージのサンプルを用意しました。

まず、bzipパッケージですが、展開はできても、圧縮のAPIがないため、圧縮はできません。
単体のファイルを圧縮するサンプルがbzip.goです。テキストファイルをbzip2に圧縮したファイルを展開しています。圧縮/展開と言っても、Readerを生成して、データを読み取るだけのロジックで、非常にシンプルにできています。ファイル単体なら、最小で3行程度で展開できてしまいます。

では、複数のファイルの場合はどうするかというと、tarbzip.goのように、前回でてきた、archive/tarパッケージを使って、bzipのReaderからtarのReaderを生成し、前回のサンプルと全く同じ方法で読み出すだけとなります。
compressパッケージのReaderはDecoratorパターンのような記述が可能です。

flateパッケージは、圧縮/展開のAPI両方が用意されています。まず、圧縮のflate.goですが、flate.NewWriter()を呼び出すと、flateのWriterが生成されます。Writerが圧縮処理を担当するので、呼び出し側は、何も考えずに圧縮対象のbyteデータをWriteするだけ。となっています。Golang Cafeの前に用意した時は、byte.Bufferに一度書き込みしてから、flateのWriterに書き込みをしていましたが、io.Writerを実装している(Write([]byte)がある)のでそのままデコレータパターンで使うことができます。
同様に、readflate.goも同様です。io.ReadCloserを実装していますが、io.ReadCloserは、io.Readerとio.Closer()のCompositeなので、そのまま使うことができます。
が、io.Closer()が付いているということは、Close()があるので、サンプルコードでは何も起きませんでしたが、実際には、Close()を呼び出すために、一度変数に入れておいたほうがいいかもしれません。(flateパッケージのソースコードを呼ぶ限りだと、Close()で何もしておらず、Read時にエラーがあれば、それが返されるようです)

gzipパッケージもflateがgzipになっただけなので、gzip.goreadgzip.goをごらんください。

最終的に、最速ペースで進んでいたのですが、説明の途中に、「インターフェイスが実装されているかどうかを判断する方法が難しい」という話題になり、


確かに、Go言語は「メソッドが実装されていれば、インターフェイスを実装している」と扱われるため、実際には実装されている(今回のパターンだと、Write([]byte)が実装されているので、io.Writerとして扱える)が、プログラマの誤解により、「型が違うから別のもの」と判断してしまうことがあります。これを何とか回避する方法を考えていると

Type Assertionを使って判定する方法がある。というヒントを得ました。
いろいろ試行錯誤している間に時間が過ぎてしまって、曖昧なまま終了しました。

私自身は、Type Assertionを使ったコードを書いて、確認し、2つ目の戻り値がtrueなら実装されている事がわかるので、その後、コードを書き直す。という理解をしたのですが、(コード作成時なのと、標準パッケージなので、実際にリリースするアプリケーションにそんな意味のないコードが必要とは思えないし…)

Type Assertionをしっかり理解していないとなかなか難しいところです。

さて、次回は、containerパッケージを見ていく予定です。