Goのinterface - 使う側で定義する
Goのinterfaceは「使う側」が定義するもの
前回Formatter interface、Formatterを使ってレポートを出力するReport struct、
Formatterの実装PlainTextFormatterとHTMLFormatterを定義しました。
type Formatter interface {
OutputStart()
OutputHead(text string)
OutputBodyStart()
OutputLine(line string)
OutputBodyEnd()
OutputEnd()
}
type Report struct {
Title string
Text []string
Formatter Formatter
}
type PlainTextFormatter struct{}
// (中略)PlainTextFormatterのメソッド定義
type HTMLFormatter struct{}
// (中略)PlainTextFormatterのメソッド定義
Goのinterfaceは、Javaなどのインターフェイスや基底クラスとは異なります。
Javaのインターフェイスは、具象型を定義するとき(class 〜 implements 〜)と、
変数を定義するときの両方に定義する必要があります。
一方、Goでは具象型の定義ではinterfaceの指定の必要はありません。
| Go | Java | |
|---|---|---|
| 具象型の定義 | なし! |
class PlainTextFormatter implements Formatter {}
|
| 変数定義 | var fmt Formatter = &PlainTextFormatter{}
| Formatter fmt = new PlainTextFormatter();
|
Goのinterfaceは純粋に変数(受け手・使用する側)での型を指定するものです。
その点、interfaceはC++のTraitsや関数型言語での型クラスに似ており、
interfaceを使ったコードは総称型プログラミングのコードだとと言えます。
interfaceのtips
interfaceは「使用する側」のためのものですから、すべてのメソッドを列挙する必要はありません。必要なものだけ指定すればよいのです。
// Formatterの必要なメソッドだけ取り出したinterface
type HeadFormatter interface {
OutputStart()
OutputHead(text string)
}
type HeadReport struct {
Title string
HeadFormatter HeadFormatter
}
r := &Report{}
r.HeadFormatter = &PlainTextFormatter{} // 当然、代入できる
また、interfaceは「使用する側」のためのものですから、1回しか使わないinterfaceには名前を付けずに済ませることができます。
type HeadReport struct {
Title string
HeadFormatter interface {
OutputStart()
OutputHead(text string)
}
}
この、1. 必要なメソッドだけを、2.無名interfaceとして定義する 方法は積極的に使うべきです。たとえば以下のような効能があります。
- 具象型を定義しているパッケージをimportする必要が無くなり、コードが粗結合になる
- メソッド数が最小限になるので、具象型を実装しやすい
- 特にテスト時にモックオブジェクトを作りやすくなる
もちろん、http.ResponseWriterのような頻出・メソッド数が多いinterfaceは型を定義する側が提供した方が良いこともあります。