🦔

Golangのinterfaceについてストーリー仕立てに解説してみる

2022/03/24に公開

お久しぶりです!

お久しぶりです!お久しぶりすぎです!!!

残念ながら、モバイル関連の記事ではありません。
というか、モバイル関連の記事書いたことねぇわー、それでよくモバイルやってますとか言えるわ―、神経疑うわ―
それではいきます!

なぜこんな記事を書くのか

interfaceを説明できない

Golangのinterface{}を、プログラミングの知識が浅い人に説明するにはどうしたらよいかを考えたとき、まず最初に

  • 何でもいれていいやーつ

という説明が思いつきました。
ただちょっと待て、それではあまりに乱暴だしそういうものではないよなと思い、どう説明すれば伝わるかを考えること2分...クソ難しいじゃねぇか!!
これは具体例を出すしかない、これは具体例を出すしかない!!!となり、この記事を書いています。

ストーリー仕立てにする必要ある?

「抽象化して、このstructでは取得したい情報が限られるので...」
というようなよくある解説方法でも良いのですが、ちょっと自分ごとと思えず頭に入ってこないことがよくあるので、具体的に想像できるストーリーがある方が、ユースケースが見えやすいのかな?ということでストーリー仕立てにしています。

サンプルコード

go-interface-sample - GitHub
README.mdにストーリーが書いてあるので、もはやサンプルコードを見てもらうのがいいと思っています。

ストーリー

ここは読み飛ばしてもいいですが、おそらくこの記事で一番おもしろいところになると思います。

Side:Zeon - 悩みの尽きない今日このごろ

これまで宙域のモビルスーツ(MS)戦において、ジオンは常に優勢であった。
ところがここ1ヶ月あたりの実績はどうだろうか?
認めたくないものの、地球連邦側のMS導入実績は眼を見張るものがある。
まだまだパイロットの実力という部分では引けを取らないものの、一撃でザクIIを沈める武器を装備されてしまっては...

少佐は「当たらなければどうということはない」と仰るものの、全くの無傷で戦線を乗り切るのは一流以上のパイロットでなければ難しい。
戦線が広がる中、ジュニアパイロットを動員せざるを得ないこの状況、打開するにはどうしたものか...

おそらくビーム兵器の装備により、地球連邦側の戦力は急激に増大したと言っても過言ではない。
まだ実績が少なく安全性が危ぶまれる中、ビーム兵器の導入を決意したとは。
ジオンとしてもこのままではいられない。
ビーム兵器を標準搭載したMSの導入に踏み切ることにしよう。
ジオニック社の営業担当の連絡先はどこだったかな...

丁度いい。この件は新担当のアイツに任せることにしよう。

Side:ZEONIC - 面倒くさがりの営業担当

あれあれ?最近お財布事情が厳しそうなジオンさんからご連絡ですか。珍しい。
ん?なんかすげーメール長ぇな。読むのめんどくせ...

とりあえずうちの汎用カタログそのまんま送りつけとくかー
真面目だからかってに提案依頼書とか作ってくるやろ。

Side:Zeon - ジオニックからの資料を受けて

いやはや、まだ調達担当になって2週間。
こんなに細かいスペックが記載された資料を受け取ってもな...

とりあえず、宙域戦での使用ができて、ビーム兵器積んでりゃいいんだよな?
あー!ここ、ここ!この宙域欄と、兵器欄をチェックして要件に合うやつをピックアップすればいいんだな!

あとは詳細な提案に来てもらって決めることにしよう。
絶対にジオニック来訪時はあの上司野郎を同席させる...!丸投げしやがって!

Side:ZEONIC - 資料を読まない営業担当

お、返事きたきた。期待通り提案依頼書つけてくれてるぜ!
あ?いちいちなげーんだよ!提案してほしいMSのコードだけ拾い上げてっと...

なるほど、なるほど。
この手のスペックをご所望だとは...
ジオンの新担当さんとやらは目の付け所がいいようだ。

となると...ジオンの状況から鑑みてもこいつを提案してしまっても大丈夫そうだな...
資料上は同列として出しておけばいいが、訪問時にはこいつを推していくことにしよう。

そして始まるジオニックの提案

ジオニック社はジオンからの提案依頼書を受け、社内で大きな物議を醸す、あの新作を提案の中心において本件を進めることに。

このやり取りが、のちのあの事件を巻き起こすことになるとは、今この時点で誰も知る由がなかった。

解説

どこをinterfaceにするのか

今回の場合、ジオニックの営業担当とジオンの新任調達担当の間のモビルスーツ(MS)の仕様のやり取りの部分でinterface{}を使用しています。

  • ジオニック側:具体的にstructを宣言している
    type zeonicMobileSuite struct {
        Spec    *mobilesuite.Spec
        // 以降、何個かのフィールド
    }
    
    func (aems *zeonicMobileSuite) AvailableInSpace() bool {
        // 略
    }
    func (aems *zeonicMobileSuite) WithBeamWepon() bool {
        // 略
    }
    func (aems *zeonicMobileSuite) GetCode() string {
        // 略
    }
    // その他にも、`zeonicMobileSuite`は何個かのfuncを持っている
    
  • ジオン側:interface{}でMS情報を抽象化している
    type ZeonMS interface {
        AvailableInSpace() bool
        WithBeamWepon() bool
        GetCode() string
    }
    

ジオニック側は、MSについて様々な情報を準備しており、ジオニック側を完全に理解(import)する ことで、MSに関する判定を色々できるようになります。
ただ、ジオン側からすると、

とりあえず、宙域戦での使用ができて、ビーム兵器積んでりゃいいんだよな?

なので、ジオニック側の細かいそれを理解したいと思っていません
できる限り最小の情報(自ら定義したinterface{})で、判断したいと思っています。

このとき、ジオニックのMS情報がジオン側のinterface{}を満たしてくれることで、ジオンはジオニックに依存することなく、ほしいMSをピックアップできるようになります。

何が嬉しいの?

このあとの展開として、地球連邦側の息がかかったアナハイム・エレクトロニクス社にも提案依頼を出す可能性があります。
アナハイムの実装を行うとき、同様にtype ZeonMS interfaceを満たすMS情報を実装すれば、ジオン側は実装済みのzeon.Check()で要求するMSのピックアップが可能です。

func Check(ms ZeonMS) bool {
	if ms.AvailableInSpace() && ms.WithBeamWepon() {
		return true
	}
	return false
}

つまり、ジオン側では追加の判定ロジックを実装する必要がないということになります。

連邦側の情報を横取りしたときも、連邦のMS情報がinterface{}を満たしていれば、同じzeon.Check()が使えます。

以上から、interface{}を使うとpackage同士の依存度を下げ、お互いに影響の与えにくいコードとしやすくなることがわかると思います。
何か変更があったとき、修正の範囲が小さいのは嬉しいことですよね。

これ以降の理解に関しては、実際にプロダクションコードを書き、「抽象化が必要だっ!!」と思ったタイミングで良いと思います。
とりあえずイメージを掴んでいただければ。

最後に

わかりやす過ぎる!!!!!
と個人的には思ってるけど、本当か?そもそも「ジオニックとかなに?」ってなってない?
不安なってきた😜

Discussion