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: Task interfaceをDefineし、Summary() string methodを要求します。このinterfaceは、すべてのタスクの種類がfulfill mustするcommon contractになります。
  • types.go: それぞれがTask interfaceをImplementする、3つの異なるタスクの種類をCreateします。
    • BugFixにはID (string) とSeverity (string) のfieldsがあります。そのSummary()Bug #[ID] ([Severity])を返します
    • FeatureにはName (string) とPoints (int) のfieldsがあります。そのSummary()Feature: [Name] - [Points] ptsを返します
    • DocumentationにはTopic (string) fieldがあります。そのSummary()Docs: [Topic]を返します
  • main.go: Taskのスライスをacceptsし、各タスクのsummaryをそれぞれ1行にprintsする、PrintBacklogという名前のfunctionをCreateします。inputからタスクの詳細を読み取り、各タスクの種類をoneつずつCreateし、それらをすべて単一の[]TaskスライスにCollectして、PrintBacklogに渡します。

次のinputが提供されます。

  • Line 1: Bug ID
  • Line 2: Bug severity
  • Line 3: Feature name
  • Line 4: Feature points (integer)
  • Line 5: Documentation topic

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

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

ここでの利点は、PrintBacklogがbug、feature、documentationごとに別々のロジックを必要としないことです。単にスライスを反復処理し、各要素でSummary()を呼び出します。各タスクの種類は独自の形式で応答し、ポリモーフィックなコレクションによって、多様な型を統一的に処理できることを示しています。

自分で試してみよう

package main

import (
	"fmt"
)

// TODO: Task のスライスを受け取る PrintBacklog 関数を作成する
// そして各タスクの summary をそれぞれの行に出力する

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: 各タスクタイプを1つずつ作成する (BugFix, Feature, Documentation)

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

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

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

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

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