概要
とあるPRで、構造体をインターフェース化するように言われたけど、愚直に実装しようとすると処理が共通なところ多いなぁ・・・なんとかできないかなぁ・・・と調べているとで、Goだと埋め込み(embed)がこの事態の解決にもっともふさわしそうということで、マジメにまとめてみた。
埋め込み
Goで「委譲」を実現する機能。
- サンプルコード(The Go Playground)
-
アクセスレベルについてもっと調べてみたサンプルコード
- 話それますが、Go PlayGroundって、別パッケージにすることもできるんですね・・・
継承と委譲
- 継承は「is-a」、委譲は「has-a」の関係。
- 継承の目的は既存のコードを拡張して新しいクラスを作ること、委譲の目的はその名の通り、一部の機能の処理をおまかせすること。
- Goのembedは「委譲」にあたり「継承」はできないので、概念的に「A is B」な構造体間の関係だったとしても、「A has B」として実装する形になりそう。親構造体を子構造体がフィールドとして持つ感じ。
構造体の埋め込みとインターフェースとのあわせ技
実務での要件的に、以下のような感じで設計することがあったので、将来的な参考としてメモ。
-
インターフェース定義(型定義はexported)
- 外部公開したいメソッドをexportedで定義する
- データを参照したいものはすべてGetterとして定義しておく
- 外部公開したいメソッドをexportedで定義する
- 埋め込み元の構造体定義(継承でいうところの親クラス的な位置づけ)(型定義はunexported)
- インターフェースの実装となるメソッドの定義(※インターフェース定義を単独で満たす)(型定義はunexported)
- これらを支えるサブルーチンメソッドの定義(unexported)
- 処理に必要になるフィールドの定義(unexported)
- コンストラクタを定義(返り値は構造体)
- インターフェースの実装となるメソッドの定義(※インターフェース定義を単独で満たす)(型定義はunexported)
- 埋め込み先の構造体定義(継承でいうところの子クラス的な位置づけ)(型定義はunexported)
- 埋め込み元の構造体をフィールドとして定義する
- 固有の処理につかいたいフィールドの定義(unexported)
- オーバーライド的に定義したいメソッド(exported)や、それらを支える独自のメソッド(unexported)を定義する
- コンストラクタを定義(返り値はインターフェース)
参考