ポリモーフィックなコレクション
CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 44/107。
インターフェースの最も強力な活用法の一つは、同じコレクションに異なる型を格納することです。インターフェース型のスライスには、そのインターフェースを満たす任意の値を格納できるため、関連性はあるものの異なる型をまとめて扱えます。
さまざまな図形を管理する必要がある場面を考えてみましょう。型ごとに別々のスライスを使う代わりに、インターフェース型の単一のスライスを使うことができます。
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つの異なるタスク型を作成します。ID(string)フィールドとSeverity(string)フィールドを持つBugFix。そのSummary()はBug #[ID] ([Severity])を返します。Name(string)フィールドとPoints(int)フィールドを持つFeature。そのSummary()はFeature: [Name] - [Points] ptsを返します。Topic(string)フィールドを持つDocumentation。そのSummary()はDocs: [Topic]を返します。
main.go:Taskのスライスを受け取り、各タスクの概要をそれぞれ別の行に出力するPrintBacklogという関数を作成します。入力からタスクの詳細を読み取り、各タスク型を1つずつ作成し、それらすべてを1つの[]Taskスライスにまとめて、PrintBacklogに渡します。
次の入力が与えられます。
- 1行目:バグのID
- 2行目:バグの深刻度
- 3行目:機能の名前
- 4行目:機能のポイント数(整数)
- 5行目:ドキュメントのトピック
たとえば、1042、critical、Themes、8、APIが与えられた場合、出力は次のようになります。
Bug #1042 (critical)
Feature: Themes - 8 pts
Docs: APIここで重要なのは、PrintBacklogがバグ、機能、ドキュメントごとに別々の処理を必要としないことです。単にスライスを反復処理し、各要素に対してSummary()を呼び出します。それぞれのタスク型が独自の形式で応答するため、ポリモーフィックなコレクションを使えば、多様な型を統一的に処理できることがわかります。
自分で試してみよう
package main
import (
"fmt"
)
// TODO: Task のスライスを受け取る PrintBacklog 関数を作成する
// そして、各タスクの概要をそれぞれ1行に出力する
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: すべてのタスクを1つの []Task スライスにまとめる
// TODO: タスクのスライスを渡して PrintBacklog を呼び出す
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
1Go オブジェクト指向の基礎
外部ファイルGo ワークスペースとモジュールパッケージとインポート公開された名前と非公開の名前Go におけるオブジェクト指向入門クラスとしての構造体構造体へのメソッド定義ポインタレシーバと値レシーバ構造体の初期化コンストラクタ関数まとめ:簡易計算機8エラー処理とOOP
error インターフェースカスタムエラー型エラーラッピング (fmt.Errorf)センチネルエラーerrors.Is() と errors.As()Panic、Defer、Recover復習 - ファイルパーサー自分で練習してみよう: Goオンラインコンパイラ