インターフェースによるポリモーフィズム
CoddyのDartジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 58/110。
前のレッスンでは、継承によるポリモーフィズム(親クラスの型を使用して子オブジェクトを操作すること)について学びました。しかし、ポリモーフィズムはインターフェースを通じても機能し、このアプローチは多くの場合、より柔軟です。
クラスが同じインターフェースを実装している場合、継承関係がなくても、それらをポリモーフィックに扱うことができます:
abstract class Printable {
void printDetails();
}
class Invoice implements Printable {
@override
void printDetails() => print('Invoice #1234');
}
class Report implements Printable {
@override
void printDetails() => print('Monthly Report');
}
class Receipt implements Printable {
@override
void printDetails() => print('Receipt for purchase');
}
void printAll(List<Printable> items) {
for (var item in items) {
item.printDetails();
}
}
void main() {
var documents = [Invoice(), Report(), Receipt()];
printAll(documents);
}Invoice、Report、および Receipt は完全に無関係なクラスであり、親クラスを共有していません。しかし、これらはすべて Printable を実装しているため、これらを同じリストに格納し、それぞれに対して printDetails() を呼び出すことができます。
これは、継承階層を強制することなく振る舞いのコントラクトを定義できるため、非常に強力です。何を継承しているかに関わらず、どのクラスでもインターフェースを実装できます。既存のコードを修正することなく、新しい印刷可能な型を追加できるため、コードのモジュール性が高まります。単にインターフェースを実装するだけです。
チャレンジ
簡単インターフェースを通じてポリモーフィズムを実証するメディアプレーヤーシステムを構築しましょう。「再生可能」であることの意味を定義するインターフェースを作成し、継承関係がないにもかかわらず、このコントラクトを満たすすべての無関係なメディアタイプをいくつか実装します。これにより、それらを一律に扱うことができるようになります。
コードを2つのファイルに整理します:
media.dart: ここでインターフェースとメディアクラスを定義します:- 2つの抽象メソッド
play()とString getMediaType()を持つ抽象クラスPlayable Playableを実装するSongクラス。String titleとString artistを持つ必要があります。そのplay()メソッドはPlaying song: [title] by [artist]を出力し、getMediaType()は'Audio'を返します。Playableを実装するVideoクラス。String titleとint duration(分単位)を持つ必要があります。そのplay()メソッドはPlaying video: [title] ([duration] min)を出力し、getMediaType()は'Video'を返します。Playableを実装するPodcastクラス。String titleとint episodeを持つ必要があります。そのplay()メソッドはPlaying podcast: [title] - Episode [episode]を出力し、getMediaType()は'Podcast'を返します。
[Media Type]:を出力した後にそのアイテムのplay()を呼び出す関数playAll(List<Playable> playlist)を作成します。- 2つの抽象メソッド
main.dart: メディアファイルをインポートし、インターフェースベースのポリモーフィズムを実証します:- タイトルが
'Imagine'でアーティストが'John Lennon'のSong、タイトルが'Dart Tutorial'で再生時間が15のVideo、タイトルが'Tech Talk'でエピソードが42のPodcastを含むList<Playable>を作成します。 - このリストを
playAll()に渡します。
- タイトルが
Song、Video、Podcast が親クラスを共有していないことに注目してください。これらは完全に無関係な型です。しかし、すべてが Playable を実装しているため、同じリストに保存し、インターフェースを通じて一律に扱うことができます。
期待される出力:
Audio:
Playing song: Imagine by John Lennon
Video:
Playing video: Dart Tutorial (15 min)
Podcast:
Playing podcast: Tech Talk - Episode 42チートシート
ポリモーフィズムはインターフェースを通じて機能し、継承関係のない無関係なクラスを統一的に扱うことを可能にします。
同じインターフェースを実装するクラスは、ポリモーフィックに使用できます:
abstract class Printable {
void printDetails();
}
class Invoice implements Printable {
@override
void printDetails() => print('Invoice #1234');
}
class Report implements Printable {
@override
void printDetails() => print('Monthly Report');
}
void printAll(List<Printable> items) {
for (var item in items) {
item.printDetails();
}
}
void main() {
var documents = [Invoice(), Report()];
printAll(documents);
}インターフェースベースのポリモーフィズムにより、以下のことが可能になります:
- 継承階層を強制することなく、振る舞いの契約を定義する
- 無関係な型を同じコレクションに格納する
- 既存のコードを修正することなく、新しい型を追加する
- コードをよりモジュール化し、柔軟にする
自分で試してみよう
import 'media.dart';
void main() {
// TODO: 以下の内容を含む List<Playable> を作成してください:
// - タイトルが 'Imagine'、アーティストが 'John Lennon' の Song
// - タイトルが 'Dart Tutorial'、再生時間が 15 の Video
// - タイトルが 'Tech Talk'、エピソード番号が 42 の Podcast
// TODO: そのリストを playAll() に渡してください
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
4Null Safety
Null Safety 入門Nullable と Non-Nullable? 演算子と ! 演算子late キーワードと Null SafetyNull-Aware 演算子クラスにおける Null Safetyまとめ:ユーザープロフィールシステム10コレクションとジェネリクス
List, Set, Map の概要型安全なコレクションジェネリッククラスジェネリックメソッドジェネリクスの制約Iterable と Iteratorまとめ:ジェネリックなストレージ