時間計測コード
最初はこう書いていた。
func (proxy *DB) Exec(query string, args ...interface{}) (sql.Result, error) {
if Enable {
startTime := time.Now()
}
ret, err := proxy.Original.Exec(query, args...)
if Enable {
proxy.measure(startTime, query)
}
return ret, err
}
が、golang なら defer があるので、それを使うとすっきりする。
func (proxy *DB) Exec(query string, args ...interface{}) (sql.Result, error) {
if Enable {
startTime := time.Now()
defer proxy.measure(startTime, query)
}
return proxy.Original.Exec(query, args...)
}
startTime と elapsedTime (proxy.measure
の呼び出し) の計測箇所を1つにまとめられて良い。
埋め込みを使うと共有インスタンスへの委譲はできない
golang で委譲といったら埋め込みであるが
type Foo struct {
Embeded
}
type Bar struct {
Embeded
}
foo := &Foo{}
bar := &Bar{}
のようにしてしまうと、当然であるが、Foo インスタンス、Bar インスタンスそれぞれに対して、Embeded インスタンスが1つずつできる。
Foo インスタンス、Bar インスタンスで共通のインスタンスに委譲したい場合は、
type Foo struct {
embeded *Embeded
}
type Bar struct {
embeded *Embeded
}
embeded := &Embeded{}
foo := &Foo{embeded}
bar := &Bar{embeded}
のように埋め込みをやめて、通常の has-a 関係にして、同一のインスタンスを設定するしかない。埋め込みではなくなるので、メソッドを自分で1つずつラップして委譲を実現するしかない。
go-sql-metrics では db の複数インスタンスが connection pool されて使い回される可能性、もしくは都度接続するために毎回作り直される可能性があるため、インスタンスごとに metrics オブジェクトがリセットされてしまわないように、共通の metrics オブジェクトを持たせたかった。そのため、こうする必要があった。
追記:コメントで、ポインタの Embed にすればインスタンスを渡せると教えてもらった。
type Foo struct { *Embeded } type Bar struct { *Embeded } embeded := &Embeded{} foo := &Foo{embeded} bar := &Bar{embeded}
なるほど!
しかし、よくよく考えたら、go-sql_metrics では、db.Original のように生の委譲オブジェクトを取り出せるインターフェースを用意しておいてあげたいという要件があるので、結局
type Foo struct { Original *Embeded }
interface は後付け定義できる
Java のイメージで interface というと
interface TemplateInterface {}
class HtmlTemplate implements TemplateInterface {}
class TextTempalte implements TempalteInterface {}
のように、まず interface 定義があったうえで、class が定義されるものだった。
go-template_metrics を作る際に、html/template, text/template 両方に対応したかったが、 構造体定義をみても共通の interface が定義されていなかったので、Java のイメージで「interface ないのか、じゃあ無理だな」と思ってしまっていた。
ところが、golang では interface を後付けで定義することができる。go-template-metrics では
type templateInterface interface {
Execute(wr io.Writer, data interface{}) error
ExecuteTemplate(wr io.Writer, base string, data interface{}) error
}
のように interface を内部で定義し、メソッドの定義をこの interface に対して行う事で、html/template, text/template 両方に対応したメトリクスライブラリを作る事ができた。
interface を後で自由に定義して静的ダックタイピングできるというのは自分にとって大きな気付きであった。