Menu
Coddy logo textTech

暗黙的な実装

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)
}

FileWriterimplements 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 のコードは疎結合で柔軟になります。型はあらかじめインターフェースを知っておく必要がないため、独立した部品からシステムを構成しやすくなります。

challenge icon

チャレンジ

簡単

Goで型がどのように暗黙的にインターフェースを満たすかを示すメッセージ配信システムを構築しましょう。関係を明示的に宣言することなく、同じインターフェースを満たすすべての異なるメッセンジャー型を作成します。

コードを2つのファイルに分けて整理します。

  • messengers.go:単一のメソッド Send(message string) string を持つ Messenger インターフェースを Define します。次に、このインターフェースを暗黙的に満たす2つの構造体を作成します。
    • EmailMessengerAddress field はstring)
    • SMSMessengerPhoneNumber field はstring)
    各構造体には、配信内容を説明するstringを返す Send method が必要です。どちらの構造体も、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.com555-1234Hello! が与えられた場合、出力は次のようになります。

Email to alice@example.com: Hello!
SMS to 555-1234: Hello!

ここでの重要な点は、DeliverMessage function が任意の Messenger をacceptsし、EmailMessengerSMSMessenger は、一致する 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 を使って各メッセンジャー経由でメッセージを送信する
	// そして結果を出力する
}
quiz icon腕試し

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

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

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