実装 vs 継承
CoddyのDartジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 47/110。
extends と implements の両方を理解したところで、それぞれをいつ使うべきかを明確にしましょう。
動作を継承したいときは extends を使用します。サブクラスは親クラスのコードをすべて無料で取得し、それを基に構築できます。
class Animal {
void breathe() {
print('Breathing...');
}
}
class Dog extends Animal {
void bark() {
print('Woof!');
}
}
void main() {
var dog = Dog();
dog.breathe(); // 継承された - 自動的に動作する
dog.bark();
}コードを継承せずに構造を保証したい場合は、implements を使用します。すべてのメソッドを自分で記述する必要があります。
class Robot implements Animal {
@override
void breathe() {
print('Ventilating circuits...'); // 完全にカスタムな実装
}
}重要な違い:extends は「これはこの型の一種です」と示すのに対し、implements は「これはこれが行うことを実行できます」と示します。Dog は Animal であり、同じように呼吸します。Robot は動物ではありませんが、独自のロジックで同じ契約を満たすことができます。
覚えておいてください。拡張できるクラスは1つだけですが、多くのインターフェースを実装できます。真の継承関係にはextendsを選び、柔軟性が必要な場合や実装の詳細を共有せずに契約を適用したい場合にはimplementsを選びます。
チャレンジ
簡単extends と implements のどちらを使うべきかを示す worker システムを構築しましょう。まず基本となる worker クラスを作成し、2つのアプローチを示します。一方は本当に worker 「is a」であり、behavior を継承するクラス、もう一方は worker が行うことを「can do」できるだけで、完全にカスタムされたロジックを持つクラスです。
コードを2つのファイルに整理します。
worker.dart: ここで worker の階層を定義します。String nameプロパティとコンストラクターを持つWorkerクラス。[name] is working...を出力するwork()メソッドと、[name] takes a coffee breakを出力するtakeBreak()メソッドを含めます。Workerを extends するOfficeWorkerクラス。String departmentプロパティを追加します。work()をオーバーライドして[name] is typing reports in [department]を出力しますが、継承したtakeBreak()の behavior は維持します。Workerを implements するRobotWorkerクラス。String modelプロパティを追加します。ロボットは本当の worker ではありませんが、worker が行うことを実行できるため、完全にカスタムされた implementation を提供します。work()はRobot [model] is assembling partsを出力し、takeBreak()はRobot [model] is rechargingを出力するようにします。
main.dart: worker ファイルを import し、両方のアプローチを示します。'Alice'という名前のOfficeWorkerを'Marketing'department に作成します。- モデルが
'RX-7'のRobotWorkerを作成します。 - office worker について、
work()を呼び出してからtakeBreak()を呼び出します。 - empty line を出力します。
- robot worker について、
work()を呼び出してからtakeBreak()を呼び出します。
OfficeWorker は Worker を extends しているため、takeBreak() を automatically 継承することに注目してください。つまり、本当に worker 「is a」です。一方、RobotWorker は interface を implements するだけなので、すべてについて独自の implementation を提供する must があります。worker が行うことを「can do」しますが、独自のロボットらしい方法で実行します。
Expected output:
Alice is typing reports in Marketing
Alice takes a coffee break
Robot RX-7 is assembling parts
Robot RX-7 is recharging自分で試してみよう
import 'worker.dart';
void main() {
// TODO: 'Marketing' 部門に 'Alice' という名前の OfficeWorker を作成する
// TODO: モデル 'RX-7' の RobotWorker を作成する
// TODO: オフィスワーカーに対して work() を呼び出してから takeBreak() を呼び出す
// TODO: 空行を出力する
// TODO: ロボットワーカーに対して work() を呼び出してから takeBreak() を呼び出す
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
4Null Safety
Null Safety 入門Nullable と Non-Nullable? 演算子と ! 演算子late キーワードと Null SafetyNull-Aware 演算子クラスにおける Null Safetyまとめ:ユーザープロフィールシステム10コレクションとジェネリクス
List, Set, Map の概要型安全なコレクションジェネリッククラスジェネリックメソッドジェネリクスの制約Iterable と Iteratorまとめ:ジェネリックなストレージ自分で練習してみよう: Dartオンラインコンパイラ