暗黙的な実装
CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 26/107。
Java や C# のような多くのオブジェクト指向言語では、implements のようなキーワードを使って、ある型が interface を実装することを explicitly 宣言しなければなりません。Go は異なるアプローチを取ります。interface の実装は暗黙的です。
型は、そのインターフェースのすべてのメソッドを実装するだけで、インターフェースを満たします。どこにも関係を declare する必要はありません。コンパイラが自動的に判断します。
type Writer interface {
Write(data string) int
}
type FileWriter struct {
Filename string
}
// FileWriter は暗黙的に Writer を実装します
func (f FileWriter) Write(data string) int {
fmt.Println("Writing to", f.Filename)
return len(data)
}
FileWriter に implements Writer 宣言がないことに注目してください。FileWriter にはインターフェースのシグネチャーに一致する Write(data string) int メソッドがあるため、Writer インターフェースを自動的に満たします。
この設計には強力な効果があります。型がすでに存在した後で、インターフェースを定義できるのです。サードパーティーライブラリに必要なメソッドを持つ型がある場合、元のコードを変更せずに、その型がすでに満たしているインターフェースを作成できます。
func Save(w Writer, content string) {
w.Write(content)
}
func main() {
fw := FileWriter{Filename: "data.txt"}
Save(fw, "Hello!") // FileWriter が Writer を満たすため動作する
}
暗黙的な実装により、Go のコードは疎結合で柔軟になります。型はあらかじめインターフェースを知っておく必要がないため、独立した部品からシステムを構成しやすくなります。
チャレンジ
簡単Goで型がどのように暗黙的にインターフェースを満たすかを示すメッセージ配信システムを構築しましょう。関係を明示的に宣言することなく、同じインターフェースを満たすすべての異なるメッセンジャー型を作成します。
コードを2つのファイルに分けて整理します。
messengers.go:単一のメソッドSend(message string) stringを持つMessengerインターフェースを Define します。次に、このインターフェースを暗黙的に満たす2つの構造体を作成します。EmailMessenger(Addressfield はstring)SMSMessenger(PhoneNumberfield はstring)
Sendmethod が必要です。どちらの構造体も、Messengerを実装することを明示的に宣言してはいけません。Goはmethod signaturesに基づいて、それをautomaticallyに判断します。main.go:任意のMessengerとmessage stringをacceptsし、Sendをcallingした結果を返すDeliverMessageという名前のfunctionを作成します。入力から連絡先情報を読み取り、両方のメッセンジャー型を作成し、functionを使用してそれぞれを通じてメッセージを配信します。
次の入力が提供されます。
- 1行目:Email address
- 2行目:Phone number
- 3行目:送信するMessage
Send methods は、次の形式のstringを返す必要があります。
- EmailMessenger:
Email to [Address]: [message] - SMSMessenger:
SMS to [PhoneNumber]: [message]
たとえば、alice@example.com、555-1234、Hello! が与えられた場合、出力は次のようになります。
Email to alice@example.com: Hello!
SMS to 555-1234: Hello!ここでの重要な点は、DeliverMessage function が任意の Messenger をacceptsし、EmailMessenger と SMSMessenger は、一致する Send method を持つだけでそのインターフェースを満たすということです。implements keyword はどこにもありません。Goの暗黙的な実装がすべて処理します。
自分で試してみよう
package main
import (
"bufio"
"fmt"
"os"
)
// TODO: 任意の Messenger を受け取る DeliverMessage 関数を作成する
// とメッセージ文字列を受け取り、Send を呼び出した結果を返す
func main() {
reader := bufio.NewReader(os.Stdin)
// Read email address
email, _ := reader.ReadString('\n')
email = email[:len(email)-1]
// 電話番号を読み取る
phone, _ := reader.ReadString('\n')
phone = phone[:len(phone)-1]
// メッセージを読み取る
message, _ := reader.ReadString('\n')
if len(message) > 0 && message[len(message)-1] == '\n' {
message = message[:len(message)-1]
}
// TODO: メールアドレスで EmailMessenger を作成する
// TODO: 電話番号で SMSMessenger を作成する
// TODO: DeliverMessage を使って各メッセンジャー経由でメッセージを送信する
// そして結果を出力する
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
1Go オブジェクト指向の基礎
外部ファイルGo ワークスペースとモジュールパッケージとインポート公開された名前と非公開の名前Go におけるオブジェクト指向入門クラスとしての構造体構造体へのメソッド定義ポインタレシーバと値レシーバ構造体の初期化コンストラクタ関数まとめ:簡易計算機8エラー処理とOOP
error インターフェースカスタムエラー型エラーラッピング (fmt.Errorf)センチネルエラーerrors.Is() と errors.As()Panic、Defer、Recover復習 - ファイルパーサー自分で練習してみよう: Goオンラインコンパイラ