メソッドの昇格
CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 36/107。
struct の埋め込みによってフィールドが外側の struct に昇格されるのと同じように、Go は埋め込まれた型のメソッドも昇格させます。つまり、埋め込まれた struct に定義されたメソッドを、外側の struct 上で直接呼び出すことができます。
type Engine struct {
Horsepower int
}
func (e Engine) Start() string {
return "Engine started"
}
type Car struct {
Model string
Engine // 埋め込み
}
Car は Engine を埋め込んでいるため、Start() method は自動的に昇格されます:
func main() {
c := Car{
Model: "Sedan",
Engine: Engine{Horsepower: 200},
}
fmt.Println(c.Start()) // エンジンが始動しました
fmt.Println(c.Engine.Start()) // これも動作します
}
この昇格には、interface にとって重要な結果があります。埋め込み型が interface を満たしている場合、外側の型も自動的にその interface を満たします。
type Starter interface {
Start() string
}
func Ignite(s Starter) {
fmt.Println(s.Start())
}
func main() {
c := Car{Model: "Sedan", Engine: Engine{Horsepower: 200}}
Ignite(c) // Car は Engine を通じて Starter を満たす
}
Car 型は Starter を明示的に実装していませんが、埋め込まれた Engine に必要なメソッドがあるため、インターフェースを満たします。これが、Go が従来の継承を使わずに動作の再利用を実現する方法です。
チャレンジ
簡単埋め込み型のメソッドが外側の型にどのように昇格されるか、またそれによってコンポジションを通じた interface の満足がどのように可能になるかを示す音楽プレーヤーシステムを構築しましょう。
コードを2つのファイルに分けて整理します。
audio.go:音楽プレーヤーの構成要素を作成します。AudioPlayerstruct:Brandfield(string)と、[Brand] playing audioを返すPlay() stringmethodPlayableinterface:Play() stringmethod を要求しますSmartphonestruct:AudioPlayerを埋め込んだModelfield を持ちますTabletstruct:AudioPlayerを埋め込んだModelfield を持ちます
main.go:任意のPlayableを accepts し、Play()を calling した result を returns するStartPlaybackという名前の function を作成します。入力からデバイス情報を読み取り、Smartphone と Tablet の両方を作成します。そして、どちらの型も自身で明示的にPlay()を実装していないにもかかわらず、埋め込まれたAudioPlayerを通じてPlayableinterface を満たすことを示します。
次の入力が提供されます。
- 1行目:Smartphone の model 名
- 2行目:Smartphone の audio brand
- 3行目:Tablet の model 名
- 4行目:Tablet の audio brand
各デバイスについて、その model を出力した後、StartPlayback に渡した result を出力します。
たとえば、iPhone 15、Apple Audio、iPad Pro、Beats が与えられた場合、出力は次のようになります。
iPhone 15
Apple Audio playing audio
iPad Pro
Beats playing audioAudioPlayer の Play() method が各外側の型に自動的に昇格されるため、追加のコードなしで Playable interface を満たし、Smartphone と Tablet の両方を StartPlayback に渡せることに注目してください。
自分で試してみよう
package main
import (
"bufio"
"fmt"
"os"
)
// TODO: 任意の Playable を受け取る StartPlayback 関数を作成する
// そして Play() を呼び出した結果を返す
func main() {
scanner := bufio.NewScanner(os.Stdin)
// スマートフォンのモデルを読み取る
scanner.Scan()
smartphoneModel := scanner.Text()
// スマートフォンのオーディオブランドを読み取る
scanner.Scan()
smartphoneBrand := scanner.Text()
// タブレットのモデルを読み取る
scanner.Scan()
tabletModel := scanner.Text()
// タブレットのオーディオブランドを読み取る
scanner.Scan()
tabletBrand := scanner.Text()
// TODO: 埋め込まれた AudioPlayer を持つ Smartphone を作成する
// TODO: 埋め込まれた AudioPlayer を持つ Tablet を作成する
// TODO: スマートフォンのモデルを出力し、その後スマートフォンで StartPlayback を呼び出す
// TODO: タブレットのモデルを出力し、その後タブレットで StartPlayback を呼び出す
_ = smartphoneModel
_ = smartphoneBrand
_ = tabletModel
_ = tabletBrand
fmt.Println("TODO: Complete the implementation")
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
1Go オブジェクト指向の基礎
外部ファイルGo ワークスペースとモジュールパッケージとインポート公開された名前と非公開の名前Go におけるオブジェクト指向入門クラスとしての構造体構造体へのメソッド定義ポインタレシーバと値レシーバ構造体の初期化コンストラクタ関数まとめ:簡易計算機8エラー処理とOOP
error インターフェースカスタムエラー型エラーラッピング (fmt.Errorf)センチネルエラーerrors.Is() と errors.As()Panic、Defer、Recover復習 - ファイルパーサー自分で練習してみよう: Goオンラインコンパイラ