Menu
Coddy logo textTech

実装 vs 継承

CoddyのDartジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 47/110。

extendsimplements の両方を理解したところで、それぞれをいつ使うべきかを明確にしましょう。

振る舞いを継承したいときは、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 は「私はこれができることを実行できます」と言う点です。DogAnimal であり、同じ方法で呼吸します。Robot は動物ではありませんが、独自のロジックで同じコントラクトを満たすことができます。

覚えておいてください:継承できるクラスは1つだけですが、複数のインターフェースを実装することができます。真の継承関係には extends を選択し、柔軟性が必要な場合や、実装の詳細を共有せずに規約を強制したい場合には implements を選択してください。

challenge icon

チャレンジ

簡単

extendsimplements をいつ使うべきかを示すワーカーシステムを構築しましょう。ベースとなるワーカークラスを作成し、2つのアプローチを示します。1つは真にワーカー「である(is a)」クラスで振る舞いを継承し、もう1つはワーカーがすることを「できる(can do)」クラスですが、完全にカスタムのロジックを持ちます。

コードを2つのファイルに整理します:

  • worker.dart: ここでワーカーの階層を定義します:
    • String name プロパティとコンストラクタを持つ Worker クラス。[name] is working... と出力する work() メソッドと、[name] takes a coffee break と出力する takeBreak() メソッドを含めます。
    • Workerextends(継承)する OfficeWorker クラス。String department プロパティを追加します。work() をオーバーライドして [name] is typing reports in [department] と出力するようにしますが、継承された takeBreak() の振る舞いはそのまま保持します。
    • Workerimplements(実装)する RobotWorker クラス。String model プロパティを追加します。ロボットは厳密にはワーカーではありませんが、ワーカーと同じことができるため、完全にカスタムの実装を提供します。work()Robot [model] is assembling parts と出力し、takeBreak()Robot [model] is recharging と出力するようにします。
  • main.dart: ワーカーファイルをインポートし、両方のアプローチを実演します:
    • 'Marketing' 部署に 'Alice' という名前の OfficeWorker を作成します。
    • モデル名 'RX-7'RobotWorker を作成します。
    • オフィスワーカーに対して、work() を呼び出し、次に takeBreak() を呼び出します。
    • 空行を1行出力します。
    • ロボットワーカーに対して、work() を呼び出し、次に takeBreak() を呼び出します。

OfficeWorkerWorker を継承(extends)しているため、自動的に takeBreak() を継承していることに注目してください。これは真にワーカー「である」からです。一方、RobotWorker はインターフェースを実装(implements)しているだけなので、すべてのメソッドに対して独自の実装を提供する必要があります。これはワーカーができることを「できる」のですが、ロボット独自の方法で行うためです。

期待される出力:

Alice is typing reports in Marketing
Alice takes a coffee break

Robot RX-7 is assembling parts
Robot RX-7 is recharging

チートシート

親クラスから振る舞いを継承するには extends を使用します。サブクラスは自動的にすべての親メソッドを取得し、それらをオーバーライドできます:

class Animal {
  void breathe() {
    print('Breathing...');
  }
}

class Dog extends Animal {
  void bark() {
    print('Woof!');
  }
}

void main() {
  var dog = Dog();
  dog.breathe();  // Inherited automatically
  dog.bark();
}

コードを継承せずに構造を保証するには implements を使用します。すべてのメソッドに対して独自の定義(実装)を提供する必要があります:

class Robot implements Animal {
  @override
  void breathe() {
    print('Ventilating circuits...');
  }
}

主な違い:

  • extends:「私はこれの一種です」 - コードを共有する真の継承
  • implements:「私はこれができることと同じことができます」 - カスタムロジックによる契約の強制
  • 継承(extend)できるクラスは1つだけですが、複数のインターフェースを実装(implement)できます。

振る舞いを継承すべき真の親子関係には extends を選択してください。柔軟性が必要な場合や、実装を共有せずに契約を強制したい場合は implements を選択してください。

自分で試してみよう

import 'worker.dart';

void main() {
  // TODO: 'Marketing'部門の'Alice'という名前のOfficeWorkerを作成してください
  
  // TODO: モデル名'RX-7'のRobotWorkerを作成してください
  
  // TODO: office workerに対して、work()を呼び出し、次にtakeBreak()を呼び出してください
  
  // TODO: 空行をプリントしてください
  
  // TODO: robot workerに対して、work()を呼び出し、次にtakeBreak()を呼び出してください
}
quiz icon腕試し

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

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