ポリモーフィックなコレクション
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メッセージを送信する通知システム、さまざまな種類の敵を更新するゲーム、あるいは複数のファイル形式を扱うドキュメントプロセッサなど、多様なアイテムを処理するシステムを構築する際に不可欠です。コレクションは特定の型を気にしません。各要素が必要な振る舞いを提供していることだけを重視します。
チャレンジ
簡単統一されたインターフェースを通じて、さまざまな種類のタスクを処理するタスク管理システムを構築しましょう。多様なタスクタイプを作成し、それらをポリモーフィックなコレクションとしてまとめて処理します。
コードは以下の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を呼び出します
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
1Go オブジェクト指向の基礎
外部ファイルGo ワークスペースとモジュールパッケージとインポート公開された名前と非公開の名前Go におけるオブジェクト指向入門クラスとしての構造体構造体へのメソッド定義ポインタレシーバと値レシーバ構造体の初期化コンストラクタ関数まとめ:簡易計算機8エラー処理とOOP
error インターフェースカスタムエラー型エラーラッピング (fmt.Errorf)センチネルエラーerrors.Is() と errors.As()Panic、Defer、Recover復習 - ファイルパーサー