ちょっとgoはモックが一筋縄では以下なさそう(特に既存のパッケージをモックしたい場合)なので、その解決策をひねり出すべく、一旦継承の仕組みについて調べてみたのでまとめ。
やっぱり自分が覚えるのは書くのが一番なので。。。
main.go
package main
import (
"fmt"
"time"
)
/*
User という構造体を定義する。
これがオブジェクトの基本になるイメージ。
*/
type User struct {
Name string
}
/*
funcで始まるが、これはメソッドであり関数とは違うもの。
宣言のイメージ的に、第一引数のUserに対して、Nowというメソッドを
使えるようにしてあげますよ・・・という感じになる。
*/
func (u User) Now() time.Time {
fmt.Println("User::Now() has called")
return time.Now()
}
/*
ChikokuUser (遅刻ユーザー) という構造体を定義する。
これは継承先のオブジェクトの基本になるイメージ。
一応 ExtParam というものを追加で持っている。
*/
type ChikokuUser struct {
User
ExtParam string
}
/*
ChikokuUser の Now をオーバーライドする。
同じように ChikokuUser 構造体に対して同名のメソッドを追加し、
事実上のオーバーライドをしている。
*/
func (cu ChikokuUser) Now() time.Time {
fmt.Println("ChikokuUser::Now() has called")
n := time.Now()
// 俺的にはまだ5時間余裕がある。なので遅刻する。
n = n.Add(-5 * time.Hour)
return n
}
func main() {
// 遅刻ユーザーを宣言する。第一引数はUser構造体で、第二引数が文字列(->ExtParamになる)
cu := ChikokuUser{User{"keiichi"}, "this is ext param"}
fmt.Println(cu.ExtParam) // --> ExtParamである "this is ext param" が表示される
oretekiNow := cu.Now() // --> 俺的Nowは5時間前。overrideされた ChikokuUser::Now() が呼ばれる
layout := "2006-01-02 15:04:05 MST"
fmt.Println(oretekiNow.Format(layout)) // --> 整形して表示
}
実際に実行するとこうなる。
% date ; go run main.go
Tue Nov 20 16:14:31 JST 2018
this is ext param
ChikokuUser::Now() has called
2018-11-20 11:14:31 JST <-- 5時間前の時刻になっている
もし、この状態で、 func (cu ChikokuUser) Now() time.Time
のブロックだけをコメントアウトした上(オーバーライド解除)で実行するとこうなる。
% date ; go run main.go
Tue Nov 20 16:14:11 JST 2018
this is ext param
User::Now() has called
2018-11-20 16:14:12 JST <-- ちゃんと今の時刻
とりあえずオーバーライドは成功していたということになるのではないかと。
まずgoを覚えていて最初に理解が出来てなかったのは、 関数
と メソッド
が違うものであるということ。
オブジェクト指向ではないため、構造体にメソッドを埋め込む様なイメージで宣言する感じ。
イメージ的にPerlのblessに近い感じがした。
大筋はコード中のコメント参照。具体的には継承先のstruct側で同名メソッドを定義することで事実上オーバーライドをしている。
pythonでいうsuperとかしたい場合はどうするといいのかな・・・とは思っているが今は触れないでおく。
これで、既存のパッケージを使いつつ、引数をうまいことinterface化できればいけるのではないか・・・頭では考えているが、そう上手くいくかどうかは次回また考える。
追記
oretekiNow := cu.User.Now()
のようにすると、Python でいう super.Now()
的に呼び出すことが出来るそうです。
※ zetamatta さんありがとうございます。