Menu
Coddy logo textTech

契約としてのインターフェース

CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 27/107。

インターフェースをcontractsとして捉えると、より優れた Go プログラムを設計できます。functioninterface型を受け取るということは、「この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 が満たされているため、その違いを認識しません。

challenge icon

チャレンジ

簡単

interface が contract としてどのように機能するかを示す notification system を構築しましょう。notification を送信するとはどういうことかを Define する Notifier interface を作成し、その後、この contract をすべて尊重する複数の notification channel を Implement します。

コードを2つの file に分けて整理します。

  • notifiers.go: 1つの method Notify(message string) string を持つ Notifier interface を Define します。次に、この contract を満たす3種類の異なる struct を作成します。
    • ConsoleNotifierPrefix field)
    • FileNotifierFilename field)
    • WebhookNotifierURL field)
    各 struct は、notification がどのように送信されるかを説明する string を返すように Notify method を Implement する必要があります。
  • main.go: Notifier values の 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.txthttps://hooks.example.comServer 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 をまったく変更せずに、後から SlackNotifierEmailNotifier を追加することもできます。

自分で試してみよう

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")
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Goオンラインコンパイラ