契約としてのインターフェース
CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 27/107。
インターフェースをcontractsとして捉えると、より優れた Go プログラムを設計できます。functionがinterface型を受け取るということは、「このcontractを満たしている限り、どの具体的な型を渡されても気にしない」という意味です。
この契約に基づく考え方を使うと、特定の型ではなく振る舞いに焦点を当てた関数を書けます。さまざまなソースからデータを処理する必要がある関数を考えてみましょう。
type DataSource interface {
FetchData() string
}
func ProcessData(src DataSource) {
data := src.FetchData()
fmt.Println("Processing:", data)
}ProcessData関数は、データベース接続、APIクライアント、ファイルリーダーのいずれを受け取るかを知る必要も気にする必要もありません。入力がFetchData()を実行できることだけを必要とします。このcontractを守る型であれば、どれでも機能します。
type Database struct{ ConnectionString string }
func (d Database) FetchData() string { return "data from DB" }
type APIClient struct{ Endpoint string }
func (a APIClient) FetchData() string { return "data from API" }
func main() {
db := Database{ConnectionString: "localhost"}
api := APIClient{Endpoint: "https://api.example.com"}
ProcessData(db) // 動作します!
ProcessData(api) // こちらも動作します!
}この contract アプローチによって、コードのテストもより容易になります。テスト中は、実際のデータベースやネットワーク接続を必要とせずに、interface を満たすモック型を作成できます。テスト対象の function は、contract が満たされているため、その違いを認識しません。
チャレンジ
簡単interface が contract としてどのように機能するかを示す notification system を構築しましょう。notification を送信するとはどういうことかを Define する Notifier interface を作成し、その後、この contract をすべて尊重する複数の notification channel を Implement します。
コードを2つの file に分けて整理します。
notifiers.go: 1つの methodNotify(message string) stringを持つNotifierinterface を Define します。次に、この contract を満たす3種類の異なる struct を作成します。ConsoleNotifier(Prefixfield)FileNotifier(Filenamefield)WebhookNotifier(URLfield)
Notifymethod を Implement する必要があります。main.go:Notifiervalues の slice と message string を受け取るBroadcastAlertという名前の function を Create します。この function は各 notifier に対してNotifyを Call し、結果を print する必要があります。input から configuration を読み取り、3種類すべての notifier type を Create して、それらすべてを通じて alert を broadcast します。
次の input が提供されます。
- Line 1: Console prefix
- Line 2: Filename
- Line 3: Webhook URL
- Line 4: Alert message
Notify methods は、次の formats の string を返す必要があります。
- ConsoleNotifier:
[Prefix] [message] - FileNotifier:
Writing to [Filename]: [message] - WebhookNotifier:
POST [URL]: [message]
たとえば、ALERT:、log.txt、https://hooks.example.com、Server down! が与えられた場合、output は次のようになります。
ALERT: Server down!
Writing to log.txt: Server down!
POST https://hooks.example.com: Server down!この design の強みは、BroadcastAlert function が具体的な notifier types を知らなくても、気にしなくてもよいことです。各 notifier が Notifier contract を満たしていることだけを要求します。broadcast function をまったく変更せずに、後から SlackNotifier や EmailNotifier を追加することもできます。
自分で試してみよう
package main
import (
"bufio"
"fmt"
"os"
)
// BroadcastAlertはすべてのnotifierを通じてメッセージを送信します
// TODO: この関数を次のように実装する:
// - Notifier値のスライスとメッセージ文字列を受け取る
// - 各notifierでNotifyを呼び出し、結果を出力する
func BroadcastAlert(notifiers []Notifier, message string) {
// TODO: notifierをループして各通知結果を出力する
}
func main() {
scanner := bufio.NewScanner(os.Stdin)
// コンソールのプレフィックスを読み取る
scanner.Scan()
prefix := scanner.Text()
// ファイル名を読み取る
scanner.Scan()
filename := scanner.Text()
// webhook URLを読み取る
scanner.Scan()
url := scanner.Text()
// アラートメッセージを読み取る
scanner.Scan()
message := scanner.Text()
// TODO: 3つのnotifier型すべてのインスタンスを作成する
// - プレフィックスを持つConsoleNotifier
// - ファイル名を持つFileNotifier
// - URL付きのWebhookNotifier
// TODO: 3つのnotifierすべてを含むNotifierのスライスを作成する
// TODO: notifiersとmessageを渡してBroadcastAlertを呼び出す
_ = prefix
_ = filename
_ = url
_ = message
fmt.Println("TODO: Implement the solution")
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
1Go オブジェクト指向の基礎
外部ファイルGo ワークスペースとモジュールパッケージとインポート公開された名前と非公開の名前Go におけるオブジェクト指向入門クラスとしての構造体構造体へのメソッド定義ポインタレシーバと値レシーバ構造体の初期化コンストラクタ関数まとめ:簡易計算機4インターフェース
インターフェース入門暗黙的な実装契約としてのインターフェース空のインターフェース (any)型アサーション型スイッチインターフェースの合成Stringer と Error インターフェースまとめ - 図形計算機8エラー処理とOOP
error インターフェースカスタムエラー型エラーラッピング (fmt.Errorf)センチネルエラーerrors.Is() と errors.As()Panic、Defer、Recover復習 - ファイルパーサー自分で練習してみよう: Goオンラインコンパイラ