Menu
Coddy logo textTech

インターフェースによるポリモーフィズム

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

これで、MakeSoundSpeak() method を持つ any 型で動作します。

func main() {
    dog := Dog{Name: "Rex"}
    cat := Cat{Name: "Whiskers"}
    
    MakeSound(dog)  // ワン!
    MakeSound(cat)  // ニャー!
}

同じ関数呼び出しでも、実際に渡される型によって異なる動作をします。これが実際に機能しているpolymorphismです。MakeSound関数は一度だけ記述されていますが、Speakerインターフェースを満たしている限り、無制限の数の型で機能します。

このアプローチにより、コードの柔軟性と拡張性が保たれます。話す新しい型を追加する際、既存の関数を変更する必要はありません。interface を実装するだけで、自動的に機能します。

challenge icon

チャレンジ

簡単

polymorphism の動作を示す車両説明システムを構築しましょう。共通の動作を interface を通じて共有するさまざまな車両タイプを作成し、どの車両にも対応できる単一の function を記述します。

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

  • vehicles.go: Describe() string method を要求する Describer interface を Define します。次に、この interface をそれぞれ独自の方法で Implement する3つの車両タイプを作成します。
    • CarBrandModel fields を持ちます。その Describe()Car: [Brand] [Model] を返します
    • MotorcycleBrandEngineCC(int)fields を持ちます。その Describe()Motorcycle: [Brand] [EngineCC]cc を返します
    • BicycleType field を持ちます("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

たとえば、ToyotaCamryHonda600Mountain が与えられた場合、output は次のようになります。

Car: Toyota Camry
Motorcycle: Honda 600cc
Bicycle: Mountain

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

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

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

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