Menu
Coddy logo textTech

ポリモーフィックなコレクション

CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 44/107。

インターフェースの最も強力な用途の1つは、同じコレクション内に異なる型を格納することです。インターフェース型のスライスは、そのインターフェースを満たすすべての値を保持できるため、関連性はあるが異なる型をまとめてグループ化することができます。

さまざまな図形を管理する必要があるシナリオを考えてみましょう。各タイプごとに個別のスライスを用意する代わりに、インターフェース型の単一のスライスを使用できます。

type Shape interface {
    Area() float64
}

type Circle struct{ Radius float64 }
func (c Circle) Area() float64 { return 3.14159 * c.Radius * c.Radius }

type Rectangle struct{ Width, Height float64 }
func (r Rectangle) Area() float64 { return r.Width * r.Height }

func main() {
    shapes := []Shape{
        Circle{Radius: 5},
        Rectangle{Width: 4, Height: 3},
        Circle{Radius: 2},
    }
    
    for _, s := range shapes {
        fmt.Printf("Area: %.2f\n", s.Area())
    }
}

shapes スライスは、円と長方形の両方を保持します。反復処理を行う際、各要素はその実際の型に従って Area() に応答します。これはコレクションに適用されたポリモーフィズムであり、同じループですべての図形タイプを一律に処理します。

多態的なコレクション(ポリモーフィック・コレクション)は、メールやSMSメッセージを送信する通知システム、さまざまな種類の敵を更新するゲーム、あるいは複数のファイル形式を扱うドキュメントプロセッサなど、多様なアイテムを処理するシステムを構築する際に不可欠です。コレクションは特定の型を気にしません。各要素が必要な振る舞いを提供していることだけを重視します。

challenge icon

チャレンジ

簡単

統一されたインターフェースを通じて、さまざまな種類のタスクを処理するタスク管理システムを構築しましょう。多様なタスクタイプを作成し、それらをポリモーフィックなコレクションとしてまとめて処理します。

コードは以下の3つのファイルに分けて構成します:

  • task.go: Summary() string メソッドを必要とする Task インターフェースを定義します。このインターフェースは、すべてのタスクタイプが満たすべき共通の規約となります。
  • types.go: Task インターフェースを実装する3つの異なるタスクタイプを作成します:
    • BugFix: ID (string) と Severity (string) フィールドを持ち、Summary()"Bug #[ID] ([Severity])" を返します。
    • Feature: Name (string) と Points (int) フィールドを持ち、Summary()"Feature: [Name] - [Points] pts" を返します。
    • Documentation: Topic (string) フィールドを持ち、Summary()"Docs: [Topic]" を返します。
  • main.go: Task のスライスを受け取り、各タスクのサマリーをそれぞれの行に出力する PrintBacklog という関数を作成します。入力からタスクの詳細を読み取り、各タスクタイプを1つずつ作成し、それらをすべて1つの []Task スライスにまとめ、PrintBacklog に渡します。

以下の入力が提供されます:

  • 1行目: バグ ID
  • 2行目: バグの重要度 (severity)
  • 3行目: フィーチャー名
  • 4行目: フィーチャーポイント (整数)
  • 5行目: ドキュメントのトピック

例えば、1042, critical, Dark Mode, 8, API Reference が与えられた場合、出力は以下のようになります:

Bug #1042 (critical)
Feature: Dark Mode - 8 pts
Docs: API Reference

ここでの利点は、PrintBacklog がバグ、フィーチャー、ドキュメントごとに個別のロジックを必要としないことです。単にスライスを反復処理し、各要素の Summary() を呼び出すだけです。各タスクタイプは独自の形式で応答し、ポリモーフィックなコレクションによって多様なタイプを一律に処理できることを示しています。

チートシート

インターフェース型のスライスは、そのインターフェースを満たすすべての値を保持することができ、ポリモーフィックなコレクションを可能にします。

type Shape interface {
    Area() float64
}

type Circle struct{ Radius float64 }
func (c Circle) Area() float64 { return 3.14159 * c.Radius * c.Radius }

type Rectangle struct{ Width, Height float64 }
func (r Rectangle) Area() float64 { return r.Width * r.Height }

func main() {
    shapes := []Shape{
        Circle{Radius: 5},
        Rectangle{Width: 4, Height: 3},
        Circle{Radius: 2},
    }
    
    for _, s := range shapes {
        fmt.Printf("Area: %.2f\n", s.Area())
    }
}

shapes スライスは、すべて Shape インターフェースを実装している異なる型(Circle と Rectangle)を保持します。反復処理を行う際、各要素はその実際の型に従って Area() に応答します。これはコレクションに適用されたポリモーフィズムです。

このパターンは、さまざまなアイテムを均一に処理するのに役立ちます。たとえば、異なるメッセージタイプを扱う通知システム、さまざまな敵タイプを更新するゲーム、または複数のファイル形式を処理するドキュメントプロセッサなどです。

自分で試してみよう

package main

import (
	"fmt"
)

// TODO: Taskのスライスを受け取り、各タスクの概要を
// それぞれの行に出力するPrintBacklog関数を作成してください

func main() {
	// 入力を読み込む
	var bugID string
	var bugSeverity string
	var featureName string
	var featurePoints int
	var docTopic string

	fmt.Scanln(&bugID)
	fmt.Scanln(&bugSeverity)
	fmt.Scanln(&featureName)
	fmt.Scanln(&featurePoints)
	fmt.Scanln(&docTopic)

	// TODO: 各タスクタイプ(BugFix、Feature、Documentation)を1つずつ作成してください

	// TODO: すべてのタスクを単一の[]Taskスライスにまとめます

	// TODO: タスクのスライスを使用してPrintBacklogを呼び出します
}
quiz icon腕試し

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

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