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
は型を定義する側が提供した方が良いこともあります。