[Go]インターフェース

# 概要

* [前回](https://qiita.com/hasesiu/items/8c1feaffcb4ff86a0841)の続き
* 今回はインターフェースについて学習
    * インターフェースは並行実行と並んで、Go言語の特徴的な（良い意味で）概念とのこと

参考

https://www.oreilly.co.jp/books/9784814400041/#

# インターフェース

* Go言語で唯一の抽象型（実装を持たない型）で２つの特徴がある
    * メソッドの集合： 実装するべきメソッドを定義する（Javaと同じ概念）
    * 型：変数がインターフェースを基盤とする型を持つことで、任意の型の値を代入できる
        * `var i interface{}` または、`var i any`とかくことで、任意の値やメソッドを記憶できる。空インターフェースとよぶ。（tsのany型と同じ用途）
* インターフェースも型の一種。er（〜する人）で命名する

```go:インターフェースの定義
type Stringer interface {
	String() string //実装するメソッド
}
```

* Go言語が特別なのは、**暗黙的なインターフェース**である点
    * 具象型では、インターフェースを実装することを宣言しない。（Javaでいうimplemntsをしない）
    * インターフェースのメソッドを含めば、自動的に実装したことになる
    * 静的言語と動的言語の両方を併せ持っている
        * つまり、型による安全性と、型の柔軟性をもつ

```go:暗黙的なインターフェースの例
// 具象型
type LogicProvider struct{}

// 具象型のメソッド
func(lp LogicProvider) Process(data string) string {
	fmt.Println("具象型のメソッド:", data)
	return data
}

// インターフェース
type Logic interface {
	// インターフェースのメソッド定義
	Process(data string) string
}

// 利用する型
type Client struct{
	L Logic //インターフェースをメンバで定義
}

// 利用する型のメソッド
func (c Client) Program() {
	data := "use-client"
	// インターフェースのメソッド呼び出し
	c.L.Process(data)
}

func main() {
	// 実行時に、利用する側が、具象型を設定する
	c := Client {
		L: LogicProvider{},
	}
	c.Program()
}
```
* インターフェースを知っているのは、利用する側（Client)のみ。
* インターフェースを実装しているLogicProviderはそのことを知らない（暗黙的な実装）
    * 利用する側（Client)がインターフェースを定義し、利用する機能を指定する。
    * 上記の例で、インターフェースにメソッドを追加した場合、LogicProviderは、Logicインターフェースの具象型ではなくなるので、
        * Clinentを作成する際の`L: LogicProvider{}`でエラーとなる。（=型の安全性）
        * 一方で、LogicProviderの定義ではエラーは発生しない。（=型の柔軟性）

# 埋め込みインターフェース

* 構造体と同様に、インターフェースにインターフェースを埋め込むこともできる

```go:埋め込みインターフェース
type Reader interface {
	Read(p []byte) (n int, err error)
}
type Closer interface {
	Closer() error
}
type ReadCloser interface {
	Reader
	Closer
}

```

# インターフェースを受け取り、構造体を返す

* 関数の起動はインターフェースで行い、関数の出力は具体的な型にするというルール
* インターフェースを返さないようにする。（デカップリング、バージョン管理などの問題が生じる）
* ファクトリ関数を作る際も、返却する具象型の数分、関数をつくるべき。
* ただし、errorは例外らしい。（返却するerror自体がインターフェース）

# nil

* インターフェースは型と値の両方がnilの場合にnilとなる。

```go:nil
	var s *string
	fmt.Println(s == nil) // true
	var i any // interfase{}でも同じ
	fmt.Println(i == nil) // true
	i = s // 型のポインタを渡している
	fmt.Println(i == nil) // false: 値のポインタはnil、型のポインタは非nil
```

# 型アサーション

* インターフェース型の変数が具象型を持っているかを調べるのに、型アサーションを使用する。
    * `インターフェース変数名.(具象型)`の形式
* Goでは型の扱いは慎重に行っているため、2つの型が同じ基底型をもっていても型アサーションはできない
* 安全に型アサーションを利用するために、カンマOkイディオムを利用する

```go:型アサーション
type MyInt int
func main() {
	var i any // 空インターフェース
	var mine MyInt = 20
	i = mine;
	i2 := i.(MyInt) // 型アサーション
	i3 := i.(string) // 具象型が違うのでパニック
	i4 := i.(int) // 基底型は同じだが、実際の具象型はMyIntなのでパニック
    
    // 型アサーションを利用する場合、カンマokイディオムで確認する
	i5,ok := i.(int) 
	if !ok {
		err := fmt.Errorf("iの型（値:%v）が想定外",i)
		fmt.Println(err.Error())
		os.Exit(1)
	}
	fmt.Println(i2,i3,i4,i5);
}

```

# 型Switch

* インターフェースが複数の型のいずれかの場合を判定するのに型switchを利用する
    * switch文に`インターフェース型変数.(type)`を指定する
* 型switchの目的は、インターフェースを具体的な型にすることなので、シャドーイングが役に立つ
    * `switch i := i.(type)` シャドーイング：対象の変数を同名の変数に代入する

```go:型switch
func doTypeSwitch(i any)  {
	switch i := i.(type) {
	case nil:
		// ...
	case int:
		// ...
	case string:
		// ...
	}
}
```

# 関数型インターフェースによる依存性の注入

* 関数もインターフェースを実装できる

```go:関数のインターフェース実装

type Hnaler interface {
	ServerHttp(http.ResponseWriter, *http.Request)
}

// 関数インターフェースの実装
type HandlerFunc func(http.ResponseWriter, *http.Request)

func (f HandlerFunc) 	ServerHttp(w http.ResponseWriter, r *http.Request) {
	f(w, r)
}
```

* Go言語は、上記、関数インターフェースや、暗黙のインターフェースの仕組みを使うことで、追加のライブラリ不要で、依存性の注入が簡潔に記載できる

## DIの実装例（ログ記録アプリケーション）

* 1.ログ記録関数と、データ保存場所の作成

```go:DI-1
// ログを記録する関数
func LogOutput(message string) {
	fmt.Println(message)
}

// アプリのデータを保存する場所(SimpleDataStore)
type SimpleDataStore struct {
	userData map[string]string
}
func (sds SimpleDataStore) UserNameForID(userID string) (string, bool) {
	name, ok := sds.userData[userID]
	return name,ok
}

// SimpleDataStoreのインスタンスを作成するファクトリ関数
func NewSimpleDataStore() SimpleDataStore {
	return SimpleDataStore{
		userData: map[string]string{
			"1": "hase",
			"2": "taro",
			"3": "hana",
		},
	}
}
```

* 2.ビジネスロジック(ユーザを取得し、挨拶を行う)が使用するため、適合するインターフェースを作成する
    * ユーザデータに上記の保存場所（SimpleDataStore）。また、その際にログ記録関数（LogOutput)を使用するが、上記を直接使用したくない。（データ保存やログ記録を別の仕組みで行うかもしれないので）
    * そのため、ビジネスロジックが何に依存するかを説明するインターフェースを用意する

```go:DI-2
// ビジネスロジックが依存するインターフェース
type DataStore interface {
	UserNameForId(userID string)(string,bool)
}
type Logger interface {
	Log(message string)
}

// LogOutputが上記Loggerインターフェースに適合するように関数型を定義
type LoggerAdapter func(message string)
func (lg LoggerAdapter) Log(message string) {
	lg(message)
}

```

* 3.ビジネスロジックの実装

```go:DI-3
// ビジネスロジック
// 定義したフィールドはどちらも具象型（LogOutput, SimpleDataStore）とは依存していない
type SimpleLogic struct {
	l Logger
	ds DataStore
}

func (sl SimpleLogic) SayHello(userID string) (string,error) {
	sl.l.Log("SayHello(" + userID + ")")
	name, ok := sl.ds.UserNameForId(userID)
	if !ok {
		return "", errors.New("不明なユーザ")
	}
	return name + "さんこんにちは", nil

}

func (sl SimpleLogic) SayGoodbye(userID string) (string,error) {
	sl.l.Log("SayGoodbye(" + userID + ")")
	name, ok := sl.ds.UserNameForId(userID)
	if !ok {
		return "", errors.New("不明なユーザ")
	}
	return name + "さんさようなら", nil
}

// ビジネスロジックのファクトリ関数
func NewSimpleLogic(l Logger, ds DataStore) SimpleLogic {
	return SimpleLogic{
		l: l,
		ds: ds,
	}
}
```

* 4.コントローラー（ビジネスロジックの利用側）の作成

```go:DI-4
// ビジネスロジックから、コントローラーが利用するものを定義
type Logic interface {
	SayHello(userID string) (string, error)
}

// コントローラーの作成
type Controller struct {
	l Logger
	logic Logic
}
func (c Controller) HandleGreeting(w http.ResponseWriter, r *http.Request) {
	c.l.Log("SayHello内")
	userID := r.URL.Query().Get("user_id")
	message, err := c.logic.SayHello(userID)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(err.Error()))
		return
	}
	w.Write([]byte(message))
}
// コントローラのファクトリ関数
func NewController(l Logger, logic Logic) Controller {
	return Controller{
		l: l,
		logic: logic,
	}
}
```

* 5.最後にmain関数で実行

```go: DI-5
func main() {
	l := LoggerAdapter(LogOutput)
	ds := NewSimpleDataStore()
	logic := NewSimpleLogic(l,ds)
	c := NewController(l, logic)
	http.HandleFunc("/hello", c.HandleGreeting)
	http.ListenAndServe(":8080",nil)
}
```

以下で実行

> http://localhost:8080/hello?user_id=1

* すべて具象型を設定しているのはmain関数内のみなので、実装を入れ替えたい場合、書きかえるのはmain関数のみになる
* 依存性注入によって、依存を外部化することができた

# おわりに

* 暗黙的なインターフェース、依存性注入については、大切な観点なので、読み返し理解を深めていこうと思う。
* 暗黙的なインターフェースは便利な半面、インターフェースを実装（実現）してるのかわからないのは、不安に感じてしまう。
* DIについては参考書の例題が長くて、処理を追うのが大変だった。（ただでさえ、DI難しいのに）
    * もうすこし簡単な例があればよかったかも。
* JavaのDIはフレームワーク依存だったのであまり深く考えなかったが、順序だってインターフェースを作成していき、最後のmain関数で依存性を注入する際、そのシンプルさと疎結合に感動できた。
    * 自分でも使いこなせるようにがんばろう。。。