見出し画像

Implicit Interfaces | Go日記: 『Learning Go』

『Learning Go』で interface のくだりの最初の方を読んだので学習記録を残す。

Interfaces

Interface example: Stringer from fmt package.

type Stringer interface {
    String() string
}

インターフェイス Interface はメソッド methods のリストで、具象型 concrete type が interface と出会うためには methods をすべて実装しなければならない。Interface によって定義された methods 一式をメソッドセット the method set of the interface と呼ぶ。

Interface は Type-Safe な Duck Typing

What makes Go’s interfaces special is that they are implemented implicitly. A concrete type does not declare that it implements an interface. If the method set for a concrete type contains all of the methods in the method set for an interface, the concrete type implements the interface. This means that the concrete type can be assigned to a variable or field declared to be of the type of the interface. This implicit behavior makes interfaces the most interesting thing about types in Go, because they enable both type-safety and decoupling, bridging the functionality in both static and dynamic languages.

Bodner, Jon. Learning Go (p.212). O'Reilly Media. Kindle 版.

Go の interfaces のなにがすごいかと言うと、それが暗黙 implicitly に実装されること。A concrete type は interface の実装を宣言しない。自分に紐付けたい interface の条件を満たすように method set で定義されている methods を実装していく。この implicit なやり方が興味深いのは、type-safe とデカップリング(分離) decoupling の両方を可能にしていること、また、それによって静的型付言語と動的型付言語の橋渡しをしていること。

To understand why, let’s talk about why languages have interfaces. Earlier we mentioned that Design Patterns taught developers to favor composition over inheritance. Another piece of advice from the book is “Program to an interface, not an implementation.” Doing so allows you to depend on behavior, not on implementation, allowing you to swap implementations as needed. This allows your code to evolve over time, as requirements inevitably change.

Bodner, Jon. Learning Go (p.212). O'Reilly Media. Kindle 版.

そもそもなんでプログラミング言語に interfaces があるかというとデザインパターン(composition over inheritance)の話に遡る。GoF本ではこうも言ってる「Program to an interface, not an implementation.(実装ではなくインターフェースに対してプログラムしろ)」。そうすることで
 ・振る舞いによるプログラミングができる、実装によるそれではなくて。
 ・必要に応じて実装を交換することができる。
これらの利点によって、要件の変化に対応してコードが進化することが可能になる。

***

書籍ではこのあと Python と Java のコード例が示される。その後で Go は両方をブレンドした感じだ、という流れで下記の(ような)事例が示される。(書籍内のサンプルコードそのままではない。)

package main
import (
	"fmt"
)
type LogicProviderX struct{}
func (lp LogicProviderX) Proc(data string) string {
	// business logic
	return fmt.Sprintf("Logic X has DATA: %v", data)
}
type Logic interface {
	Proc(data string) string
}
type Client struct {
	L Logic
}
func (c Client) Program() {
	s := "Some data"
	p := c.L.Proc(s)
	fmt.Println(p)
}
func main() {
	c := Client{
		L: LogicProviderX{},
	}
	c.Program() // Logic X has DATA: Some data
}

Client は Logic という interface を持つビジネスロジックに依存しているのであって、特定のロジック(サンプルで言えば LogicProviderX)に依存しているのではない。Client が必要とするロジックが変わって LogicProviderY が登場したら、X と同様に interface を満たしつつ Y 独自の実装をすればよく、ロジックの切り替えは、コードをキックオフしている main 関数の中でインスタンス化されている Client = Caller の中で完結する。Interface は Caller のニーズに基づいている。

Tip

Interfaces specify what callers need. The client code defines the interface to specify what functionality it requires.

インターフェイスは、呼び出し元が必要とするものを指定します。 クライアントコードは、必要な機能を指定するためのインターフェイスを定義します。

Bodner, Jon. Learning Go (p.215). O'Reilly Media. Kindle 版.

SN


この記事が気に入ったらサポートをしてみませんか?