search
LoginSignup
41

More than 1 year has passed since last update.

posted at

updated at

[Go]1行加えるだけで interface の実装を強制するテクニック

interfaceの実装を強制する簡単なテクニックを紹介

Goのinterfaceを使って抽象的にオブジェクトを扱いたいときにメソッドの実装漏れってないでしょうか?

抽象化したい構造体が多かったり、interfaceのメソッド定義を変更した場合に簡単に発生しますよね。
今回は、それを起こさないために、Goコンパイラにinterfaceの実装漏れを伝える簡単なテクニックを共有します。

導入

DoerというDo()を実装するだけで使えるinterfaceを定義しました。
下記のコードのPersonDo()を実装しているので、Doerとして扱うことができます。

type Doer interface {
    Do()    
}

type Person struct {
    Name string    
}

func (p Person) Do() {
    fmt.Printf("My name is %s. I am Doer!!!!\n", p.Name)
}

func main() {
    p := Person{Name: "John"}
    p.Do()
}
実行結果
My name is John. I am Doer!!!!

ここまで問題は無いと思います。
それでは、DoerにメソッドGreet()を追加してみましょう。
今までDo()を実装するだけで、Doerとして扱うことができていたのに、Greet()も実装する必要が発生しました。

なので、Do()しか実装していない構造体Personは、Doerとして扱うことができませんが、プログラムは正しいです。

type Doer interface {
    Do()    
    Greet() // 追加!!!
}

type Person struct {
    Name string    
}

func (p Person) Do() {
    fmt.Printf("My name is %s. I am Doer!!!!\n", p.Name)
}

func main() {
    p := Person{Name: "John"}
    p.Do()
}

このように1ファイルに収まるシンプルさなら実装漏れにすぐに気づくはずですが、
interfaceを実装したい構造体がたくさんあったら...ファイルが分けられていたら...パッケージが分けられていたら...
なかなか気づくのは難しいと思います。

それを探すのってかなり面倒ではありませんか....?

テクニックを紹介

var _ Doer = Person{} というコードを追加するだけです。
_ は省略を表しているので、メモリも無駄になりません。

これを加えるだけで、コンパイルエラーが発生し、さらにエラーが発生しているファイル名、行列数まで教えてくれるのですぐに見つけることができます。

type Doer interface {
    Do()
    Greet()
}

var _ Doer = Person{} // たったこれだけ!

type Person struct {
    Name string
}

func (p Person) Do() {
    fmt.Printf("My name is %s. I am Doer!!!!\n", p.Name)
}

func main() {
    p := Person{Name: "John"}
    p.Do()
}
./prog.go:5:5: cannot use composite literal (type Person) as type Doer in assignment:
    Person does not implement Doer (missing Greet method)

最後に

たった1行追加するだけでinterfaceの実装漏れをふせぐことができるこのテクニックを是非使ってみてください!!

実はこれUberがおすすめしているテクニックでもあります。

他にもGoのテクニックや、Uberで推奨されているスタイルがまとまっているので気になる方はチェックしてみてください。

コードレビューをする際にめちゃくちゃ使えます!
「Uberで使われているんですよ〜」といえば、納得できそうではないでしょうか?笑

番外編

コメント欄で他のテクニックも頂いたのでまとめてみました。

コンストラクタを書く

@riita10069
私はconstructerを書くのがいいかなと思っています。

コンストラクタを書くことで、同じくコンパイラに実装漏れを検知してもらうことができます。

type Doer interface {
    Do()
    Greet()
}

// constructor を追加
func NewDoer() Doer {
    return Person{}
}

type Person struct {
    Name string
}

func (p Person) Do() {
    fmt.Printf("My name is %s. I am Doer!!!!\n", p.Name)
}

func main() {
    p := Person{Name: "John"}
    p.Do()
}

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
41