インターフェースによるポリモーフィズム
CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 41/107。
Polymorphismにより、共有インターフェースを通じて異なる型を一様に扱えます。Goでは、継承やクラス階層を使わず、完全にインターフェースを通じてこれを実現します。
functionがinterface型をパラメーターとしてacceptsする場合、そのinterfaceを実装するany具体型を渡せます。functionは具体的な型を知る必要がありません。interfaceによって定義された振る舞いだけを気にします:
type Speaker interface {
Speak() string
}
type Dog struct{ Name string }
func (d Dog) Speak() string { return "Woof!" }
type Cat struct{ Name string }
func (c Cat) Speak() string { return "Meow!" }
func MakeSound(s Speaker) {
fmt.Println(s.Speak())
}
これで、MakeSound は Speak() method を持つ any 型で動作します。
func main() {
dog := Dog{Name: "Rex"}
cat := Cat{Name: "Whiskers"}
MakeSound(dog) // ワン!
MakeSound(cat) // ニャー!
}
同じ関数呼び出しでも、実際に渡される型によって異なる動作をします。これが実際に機能しているpolymorphismです。MakeSound関数は一度だけ記述されていますが、Speakerインターフェースを満たしている限り、無制限の数の型で機能します。
このアプローチにより、コードの柔軟性と拡張性が保たれます。話す新しい型を追加する際、既存の関数を変更する必要はありません。interface を実装するだけで、自動的に機能します。
チャレンジ
簡単polymorphism の動作を示す車両説明システムを構築しましょう。共通の動作を interface を通じて共有するさまざまな車両タイプを作成し、どの車両にも対応できる単一の function を記述します。
コードを2つのファイルに分けて整理します。
vehicles.go:Describe() stringmethod を要求するDescriberinterface を Define します。次に、この interface をそれぞれ独自の方法で Implement する3つの車両タイプを作成します。Car:BrandとModelfields を持ちます。そのDescribe()はCar: [Brand] [Model]を返しますMotorcycle:BrandとEngineCC(int)fields を持ちます。そのDescribe()はMotorcycle: [Brand] [EngineCC]ccを返しますBicycle:Typefield を持ちます("Mountain" や "Road" など)。そのDescribe()はBicycle: [Type]を返します
main.go: 任意のDescriberを accepts し、Describe()を calling した結果を表示するPrintDescriptionという名前の function を Create します。input から vehicle の details を読み取り、各 vehicle type を1つずつ Create し、それぞれをPrintDescriptionに渡して、同じ function が3つの異なる type すべてで動作することを demonstrate します。
次の input が提供されます。
- Line 1: Car の Brand
- Line 2: Car の Model
- Line 3: Motorcycle の Brand
- Line 4: Motorcycle の Engine CC(integer)
- Line 5: Bicycle の Type
たとえば、Toyota、Camry、Honda、600、Mountain が与えられた場合、output は次のようになります。
Car: Toyota Camry
Motorcycle: Honda 600cc
Bicycle: MountainPrintDescription は、Car、Motorcycle、または Bicycle のどれを受け取っているかを知る必要がないことに注目してください。単に Describe() を calling するだけで、各 type がそれぞれ独自の output を返します。これが polymorphism です。1つの function で複数の behavior に対応できます。
自分で試してみよう
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
// TODO: 任意の Describer を受け取る PrintDescription という関数を作成し
// Describe() を呼び出した結果を出力する
func main() {
reader := bufio.NewReader(os.Stdin)
// 車の詳細を読み取る
carBrand, _ := reader.ReadString('\n')
carBrand = strings.TrimSpace(carBrand)
carModel, _ := reader.ReadString('\n')
carModel = strings.TrimSpace(carModel)
// オートバイの詳細を読み取る
motoBrand, _ := reader.ReadString('\n')
motoBrand = strings.TrimSpace(motoBrand)
motoEngineStr, _ := reader.ReadString('\n')
motoEngineStr = strings.TrimSpace(motoEngineStr)
motoEngine, _ := strconv.Atoi(motoEngineStr)
// 自転車の詳細を読み取る
bicycleType, _ := reader.ReadString('\n')
bicycleType = strings.TrimSpace(bicycleType)
// TODO: 入力値を使用して Car、Motorcycle、Bicycle を作成する
// TODO: ポリモーフィズムを示すために各車両に対して PrintDescription を呼び出す
fmt.Println("TODO: Print vehicle descriptions")
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
1Go オブジェクト指向の基礎
外部ファイルGo ワークスペースとモジュールパッケージとインポート公開された名前と非公開の名前Go におけるオブジェクト指向入門クラスとしての構造体構造体へのメソッド定義ポインタレシーバと値レシーバ構造体の初期化コンストラクタ関数まとめ:簡易計算機8エラー処理とOOP
error インターフェースカスタムエラー型エラーラッピング (fmt.Errorf)センチネルエラーerrors.Is() と errors.As()Panic、Defer、Recover復習 - ファイルパーサー自分で練習してみよう: Goオンラインコンパイラ