🔥

Goのインターフェースについて

に公開

インターフェースとは

特定のメソッドの集合を定義する型。Structなどで定義された具体的な型がそのインターフェースを実装するかどうかが判定される。

何がいいのか?

インターフェースを使用しなければ、コードの管理が複雑になってしまう恐れがある。インターフェースを使用しなかった場合と、使用した場合のコードを比較する。

とあるWebアプリの機能で通知機能を実装することになった。SMSとEmailの両方で通知を送信する要件とする。

  1. インターフェースを使用しなかった場合

https://go.dev/play/p/i5dnIK2jZSq

package main

import "fmt"

func SendEmailNotification(emailAddress string, message string) {
    fmt.Println("Sending email to", emailAddress, ":", message)
}

func SendSMSNotification(phoneNumber string, message string) {
    fmt.Println("Sending SMS to", phoneNumber, ":", message)
}

func main() {
    SendEmailNotification("example@example.com", "Hello via Email!")
    SendSMSNotification("123-456-7890", "Hello via SMS!")
}

問題点

  • コードの重複:各通知方法に対して、ほぼ同じ内容のコードを繰り返し書く必要がある。今後通知方法を増やすと関数の数が増え、重複したコードも増える。
  • 拡張性の低さ:新しい通知方法を追加する場合、既存のコードに新しい関数を追加し、さらに処理を増やす必要がある。
  • 保守性の低さ:各関数が独立しているため、共通処理に変更があった場合、すべての関数を個別に修正する必要がある。
  1. Interfaceを使用した場合

https://go.dev/play/p/qFAAReZ19hP

package main

import "fmt"

type Notifier interface {
	Notify() string
}

type EmailNotifier struct {
	emailAddress string
	message      string
}

func (e EmailNotifier) Notify() string {
	return fmt.Sprintf("Sending email to %s: %s\n", e.emailAddress, e.message)
}

type SMSNotifier struct {
	phoneNumber string
	message     string
}

func (s SMSNotifier) Notify() string {
	return fmt.Sprintf("Sending SMS to %s: %s\n", s.phoneNumber, s.message)
}

func main() {
	notifications := []Notifier{
		EmailNotifier{emailAddress: "example@example.com", message: "Hello via Email!"},
		SMSNotifier{phoneNumber: "123-456-7890", message: "Hello via SMS!"},
	}

	for _, n := range notifications {
		fmt.Println(n.Notify())
	}
}

メリット

  • コード再利用性の向上:Notifierインターフェース型のスライスを使用して、さまざまな通知を一度に行えるようになる。
  • 拡張性の向上:新しい通知方法を追加する場合、Notifierインターフェースを実装した新しい型を作成するだけで、新しい通知方法を簡単に追加できる。
  • 保守性の向上:すべての通知方法がNotifyメソッドを通じて統一的に処理されるため、共通の通知処理が変更される場合でも、インターフェースを実装した型だけ修正すれば良い。
  • テスト容易性:インターフェースを使用すると、モックを作成してテストが簡単になる。go-mockなどを使用して、モックの生成も容易になる。

まとめ

特徴 インターフェースあり インターフェースなし
コードの重複 最小限(共通のインターフェースを使い回し) 通知方法ごとに個別の関数が必要
拡張性 高い(新しい通知方法を簡単に追加) 低い(通知方法が増えるたびに新しい関数が必要)
保守性 高い(共通処理の修正が容易) 低い(変更が必要な場合、複数の関数を修正)
可読性 高い(統一されたインターフェースで整理) 低い(個別の関数で複雑になりやすい)
テストの容易さ 高い(モックを使ったテストが可能) 低い(テスト用のコードが冗長)

Discussion